如何解析多个页面?
我一直在试图解析一个网站表格数据到一个json文件中,如果我一个接一个地做每一页,我可以做,但看到有415页需要一段时间。如何解析多个页面?
我已经看到并阅读了很多关于此主题的StackOverflow问题,但我似乎无法修改我的脚本以便它;
- 擦伤每一页,并提取50项,每页项目IDS
- 这么做速率限制的方式,所以我不会不利服务器
- 脚本等待,直到所有的请求都这样做影响我可以将每个item + item id写入JSON文件。
我相信你应该能够使用request-promise和promise.all来做到这一点,但我无法弄清楚。
实际抓取数据没问题我只是无法制作代码,刮掉一个页面,然后转到下一个URL,并在请求之间延迟或暂停。 下面的代码是我得到的最接近的代码,但是我多次获得相同的结果,并且我无法减慢请求速率。的页面的URL的
实施例:
- http://test.com/itemlist/1
- http://test.com/itemlist/2
-
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。
输入后从@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]);
});
我发现这比递归方法更好,因为你不应该关心内存开销(我不确定,但是相信创建递归的小匿名函数应该创建闭包给外部的所有变量,并且它将存活(在标记为可用于垃圾之前)收集),直到处理完所有页面。 – 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
只是想添加一个谢谢,搜索递归的承诺真的让我在正确的道路上,并显示我我试图同步使用异步功能。 – Pheonix2105
有我猜中了:你需要从列表中请求页面时,得到它的内容,解析它不知何故,等待一段时间,并要求另一页的解析......这就是直到你已经获取所有列表中的网址。 是正确的吗? 如果是的话,做递归承诺应该比Promise.all适合,因为你将只有一个活跃的承诺及时 – skyboyer
嗨,是的,这是非常多的,你可能会告诉我一些伪代码?我不得不尝试学习很多概念,因为本来应该是一个侧面项目,并且它变得有点压倒性。我肯定需要在以后回到这个。 – Pheonix2105