如何解析多个页面?

How do I parse multiple pages?

我一直在尝试将站点 table 数据解析到 json 文件中,如果我逐页解析,我可以做到这一点,但看到有 415 页会稍等片刻。

我已经看到并阅读了很多关于这个主题的 Whosebug 问题,但我似乎无法修改我的脚本以便它;

  1. 抓取每个页面并提取每页具有项目 ID 的 50 个项目
  2. 以速率受限的方式这样做,这样我就不会对服务器产生负面影响
  3. 脚本会等待所有请求完成,这样我就可以将每个项目 + 项目 ID 写入 JSON 文件。

我相信你应该能够使用请求承诺和 promise.all 来做到这一点,但我无法弄清楚。

数据的实际抓取很好我只是无法编写代码,抓取页面,然后在请求之间延迟或暂停转到下一个 URL。 下面的代码是我得到的最接近的代码,但我多次得到相同的结果,我无法降低请求率。

页面示例 URLS:

  1. http://test.com/itemlist/1
  2. http://test.com/itemlist/2
  3. http://test.com/itemlist/3 等(最多 415)

    for (var i = 1; i <= noPages; i++) {
    urls.push({url: itemURL + i});
    console.log(itemURL + i);
    }
    
     Promise.map(urls, function(obj) {
     return rp(obj).then(function(body) {
    var $ = cheerio.load(body);
    //Some calculations again...
    rows = $('table tbody tr');
    $(rows).each(function(index, row) {
      var children = $(row).children();
      var itemName = children.eq(1).text().trim();
      var itemID = children.eq(2).text().trim();
    
      var itemObj = {
        "id" : itemID,
        "name" : itemName
      };
    
      itemArray.push(itemObj);
    });
    return itemArray;
      });
     },{concurrency : 1}).then(function(results) {
       console.log(results);
      for (var i = 0; i < results.length; i++) {
       // access the result's body via results[i]
        //console.log(results[i]);
      }
     }, function(err) {
     // handle all your errors here
      console.log(err);
    });
    

抱歉可能误解 node.js 及其模块,我并没有真正使用该语言,但我需要抓取一些数据,我真的不喜欢 python。

因为您需要请求 运行 只能一个接一个 Promise.all() 无济于事。 递归承诺(我不确定它是否正确命名)会。

function fetchAllPages(list) {
    if (!list || !list.length) return Promise. resolve(); // trivial exit
    var urlToFetch = list.pop();
    return fetchPage(urlToFetch).
        then(<wrapper that returns Promise will be resolved after delay >).
        then(function() {
            return fetchAllPages(list); // recursion! 
        });
}

这段代码仍然缺少错误处理。 我也相信 async/await:

会变得更加清晰
for(let url of urls) {
    await fetchAndProcess(url);
    await <wrapper around setTimeout>;
}

但您需要找到/编写自己的 fetch()setTimeout() 实现,它们是 async

在@skyboyer 建议使用递归承诺后,我被引导到一个名为 Sequential execution of Promises using reduce()

的 GitHub Gist

首先我创建了我的 URLS 数组

for (var i = 1; i <= noPages; i++) {
    //example urls[0] = "http://test.com/1"
    //example urls[1] = "http://test.com/2"
    urls.push(itemURL + i);
    console.log(itemURL + i);
}

然后

       var sequencePromise = urls.reduce(function(promise, url) {
         return promise.then(function(results) {
        //fetchIDsFromURL async function (it returns a promise in this case) 
         //when the promise resolves I have my page data
         return fetchIDsFromURL(url)
        .then(promiseWithDelay(9000))
        .then(itemArr => {
          results.push(itemArr);
          //calling return inside the .then method will make sure the data you want is passed onto the next
          return results;
        });
    });
}, Promise.resolve([]));



// async
function fetchIDsFromURL(url)
{
  return new Promise(function(resolve, reject){
    request(url, function(err,res, body){
      //console.log(body);
      var $ = cheerio.load(body);
      rows = $('table tbody tr');
      $(rows).each(function(index, row) {
        var children = $(row).children();
        var itemName = children.eq(1).text().trim();
        var itemID = children.eq(2).text().trim();
        var itemObj = {
          "id" : itemID,
          "name" : itemName
        };
        //push the 50 per page scraped items into an array and resolve with 
        //the array to send the data back from the promise
        itemArray.push(itemObj);
      });
      resolve(itemArray);
    });
 });
}

//returns a promise that resolves after the timeout
function promiseWithDelay(ms)
{
  let timeout =  new Promise(function(resolve, reject){
    setTimeout(function()
    {
      clearTimeout(timeout);
      resolve();
    }, ms);
  });

  return timeout;
}

然后最后在 promise 序列上调用 .then,我遇到的唯一问题是在结果中返回多个数组,每个数组中的数据相同,所以由于每个数组中的所有数据都相同,我只采用第一个包含我所有已解析的带有 ID 的项目,然后我将其写入 JSON 文件。

  sequencePromise.then(function(results){
  var lastResult = results.length;
  console.log(results[0]);
  writeToFile(results[0]);
});