Javascript 使用Puppeter在循环中爬行多个URL
我有一个URL数组可以从中提取数据:Javascript 使用Puppeter在循环中爬行多个URL,javascript,web-scraping,puppeteer,google-chrome-headless,Javascript,Web Scraping,Puppeteer,Google Chrome Headless,我有一个URL数组可以从中提取数据: urls = ['url','url','url'...] 这就是我正在做的: urls.map(async (url)=>{ await page.goto(url); await page.waitForNavigation({ waitUntil: 'networkidle' }); }) 这似乎不需要等待页面加载,而且访问所有URL的速度相当快(我甚至尝试使用page.waitFor) 我想知道我是否做了一些根本错误的事情,或者这种
urls = ['url','url','url'...]
这就是我正在做的:
urls.map(async (url)=>{
await page.goto(url);
await page.waitForNavigation({ waitUntil: 'networkidle' });
})
这似乎不需要等待页面加载,而且访问所有URL的速度相当快(我甚至尝试使用page.waitFor
)
我想知道我是否做了一些根本错误的事情,或者这种类型的功能不被建议/支持。
map
、forEach
、reduce
,等等,在继续进行迭代的迭代器的下一个元素之前,不会等待它们内部的异步操作
在执行异步操作时,有多种方法可以同步遍历迭代器的每个项,但在这种情况下,我认为最简单的方法是简单地使用普通的for
操作符,它会等待操作完成
constURL=[…]
for(设i=0;i
这将访问一个又一个url,正如您所期望的那样。如果您对使用wait/async进行串行迭代感到好奇,您可以看看这个答案:如果您发现您正在无限期地等待您的承诺,建议的解决方案是使用以下方法:
const urls = [...]
for (let i = 0; i < urls.length; i++) {
const url = urls[i];
const promise = page.waitForNavigation({ waitUntil: 'networkidle' });
await page.goto(`${url}`);
await promise;
}
constURL=[…]
for(设i=0;i
正如本文所述,我找到了实现这一目标的最佳方法
const puppeteer = require('puppeteer');
(async () => {
const urls = ['https://www.google.com/', 'https://www.google.com/']
for (let i = 0; i < urls.length; i++) {
const url = urls[i];
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto(`${url}`, { waitUntil: 'networkidle2' });
await browser.close();
}
})();
const puppeter=require('puppeter');
(异步()=>{
常量URL=['https://www.google.com/', 'https://www.google.com/']
for(设i=0;i
显示如何一次一页连续访问每个页面。但是,在执行任务时,您可能希望同时访问多个页面,也就是说,刮取特定页面不依赖于从其他页面提取的数据
有一个工具可以帮助我们实现这一点,它让我们一下子做出一系列承诺,确定哪些是成功的,并收获成果
作为一个基本示例,假设我们想要为给定一系列ID的堆栈溢出用户刮取用户名
序列号:
const puppeter=require(“木偶演员”);
(异步()=>{
const browser=wait puppeter.launch({dumpio:false});
const[page]=wait browser.pages();
常量baseURL=”https://stackoverflow.com/users";
常数startId=6243352;
常数数量=5;
常量用户名=[];
对于(设i=startId;iel.children[0]。innerText
));
}
捕获(错误){}
}
console.log(用户名.长度);
等待浏览器关闭();
})();
并行代码:
const puppeter=require(“木偶演员”);
(异步()=>{
const browser=wait puppeter.launch({dumpio:false});
常量baseURL=”https://stackoverflow.com/users";
常数startId=6243352;
常数数量=5;
const usernames=(wait Promise.allselled)(
[…数组(数量)].map(异步(\ux,i)=>{
const page=wait browser.newPage();
等待page.goto(`${baseURL}/${i+startId}`);
返回页。$eval(
“.profile user--name”,
el=>el.children[0]。innerText
);
})))
.filter(e=>e.status==“已完成”)
.map(e=>e.value)
;
console.log(用户名.长度);
等待浏览器关闭();
})();
请记住,这是一种技术,而不是保证在所有工作负载上都能提高速度的银弹。在给定的特定任务和系统上,创建更多页面的成本与网络请求的并行化之间需要进行一些实验才能找到最佳平衡
这里的示例是人为设计的,因为它不与页面动态交互,因此没有像典型的木偶演员用例中那样多的增益空间,该用例涉及网络请求和每页阻塞等待
当然,要小心速率限制和站点施加的任何其他限制(运行上面的代码可能会激怒Stack Overflow的速率限制)
对于每个任务创建<代码>页< /代码>的任务是非常昂贵的,或者您想在并行请求分派上设置一个上限,考虑使用任务队列或组合上面所示的串行和并行代码来以块发送请求。显示了这个不可知论的木偶演员的一般模式
这些模式可以扩展以处理某些页面依赖于其他页面的数据时的情况,从而形成一个新的模式另请参阅,这解释了为什么使用
map
在此线程中的原始尝试无法等待每个承诺。其他人没有提到的是,如果使用同一页面对象获取多个页面,则将其超时设置为0至关重要。否则,一旦获取了默认的30秒页面,它将超时
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.setDefaultNavigationTimeout(0);
Wierd,这将提供
wait page.goto(
${url})代码>意外标识符syntaxErrpr。@user2875289您使用的是哪个版本的节点?您需要使用7.6或更高版本才能在不进行传输的情况下进行异步/等待工作。@tomahaug我使用的是Node 8.9。问题解决了。我使用的是async/wait混合了导致syntaxError的承诺。更改为async/wait only后,它现在可以工作。谢谢您好@MehranShafqat,最好将此作为一个新问题而不是评论发布。我看不出所有这些browser.newPage()
和browser.close()
调用都有什么意义。既然你在连续工作,