如何解析多个页面?

问题描述:

我一直在试图解析一个网站表格数据到一个json文件中,如果我一个接一个地做每一页,我可以做,但看到有415页需要一段时间。如何解析多个页面?

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

  1. 擦伤每一页,并提取50项,每页项目IDS
  2. 这么做速率限制的方式,所以我不会不利服务器
  3. 脚本等待,直到所有的请求都这样做影响我可以将每个item + item id写入JSON文件。

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

实际抓取数据没问题我只是无法制作代码,刮掉一个页面,然后转到下一个URL,并在请求之间延迟或暂停。 下面的代码是我得到的最接近的代码,但是我多次获得相同的结果,并且我无法减慢请求速率。的页面的URL的

实施例:

  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。

+0

有我猜中了:你需要从列表中请求页面时,得到它的内容,解析它不知何故,等待一段时间,并要求另一页的解析......这就是直到你已经获取所有列表中的网址。 是正确的吗? 如果是的话,做递归承诺应该比Promise.all适合,因为你将只有一个活跃的承诺及时 – skyboyer

+0

嗨,是的,这是非常多的,你可能会告诉我一些伪代码?我不得不尝试学习很多概念,因为本来应该是一个侧面项目,并且它变得有点压倒性。我肯定需要在以后回到这个。 – Pheonix2105

输入后从@skyboyer使用提示递归的承诺我引到GitHub的要点叫Sequential execution of Promises using reduce()

首先我创造了我的网址的阵列

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的序列上,我唯一的问题就是在结果中返回多个数组,每个数组都有相同的数据,因为每个数组中的所有数据都是相同的,所以我只把第一个包含所有解析项的数据ID,然后我把它写到一个JSON文件中。

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

我发现这比递归方法更好,因为你不应该关心内存开销(我不确定,但是相信创建递归的小匿名函数应该创建闭包给外部的所有变量,并且它将存活(在标记为可用于垃圾之前)收集),直到处理完所有页面。 – skyboyer

因为你需要一个一个地运行请求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! 
     }); 
} 

该代码仍然缺少错误处理。 此外,我相信它可以变得更加清晰与异步/等待:

for(let url of urls) { 
    await fetchAndProcess(url); 
    await <wrapper around setTimeout>; 
} 

,但你需要找到/写自己实现fetch()setTimeout()async

+0

只是想添加一个谢谢,搜索递归的承诺真的让我在正确的道路上,并显示我我试图同步使用异步功能。 – Pheonix2105