出于性能和资源方面的原因,我尝试重用puppeteer浏览器和页面的同一个示例。我的代码结构如下:
async function createBrowser(id) {
let bench = new Date().getTime();
browser = await puppeteer.launch({headless:false, executablePath: browserPath});
let pages = await browser.pages();
let page = pages[0];
let cookiesString = await fs.readFile(cookiePath);
let cookies = JSON.parse(cookiesString);
await page.setCookie(...cookies);
console.log("\x1b[1m", `[${new Date().toLocaleTimeString()}] Browser opened successfully in ${(new Date().getTime() - bench)}ms.`, "\x1b[0m");
worker(id, page);
}
我的工人/scraper逻辑:
async function worker(id, page) {
console.log(page);
let bench = new Date().getTime();
let url = 'https://www.mypagetoscrape.xyz';
let response = await page.goto(url, {waitUntil: 'networkidle0',});
// further logic goes here
然后我第一次执行代码时,一切都很顺利,但是几分钟后,当我第二次调用worker函数时,我再也不能处理response了--尽管console.log(page)仍然打印整个页面对象。
例如,如果我想获取response.status()
,则错误消息为TypeError: Cannot read properties of null (reading 'status')
有人能解释一下我做错了什么,以及如何正确地重用相同的浏览器和页面示例吗?关闭浏览器和页面,然后再重新打开它们(在执行worker逻辑之前),会延迟大约200-300毫秒的时间,并导致更多的CPU和RAM负载,所以我真的想避免它。
非常感谢!
2条答案
按热度按时间hl0ma9xz1#
根据Puppeteer goto()文档(截至最新版本v20.1.1),如果没有一些变通方法,您想要实现的结果是不可能实现的。
注意:后藤要么抛出错误,要么返回主资源响应。唯一的例外是导航到about:blank或导航到具有不同散列的相同URL,这将成功并返回null。
然而,这只是意味着
await page.goto()
响应将返回null
,而不是导航本身无法工作。只要
.goto()
调用被解析,您就可以简单地接受导航完成,然后继续运行您自己的逻辑。如果您确实需要获取初始
response
对象,则应该使用不同的方法。在这个Github问题中有一些建议:https://github.com/puppeteer/puppeteer/issues/2479。其中一个似乎可以工作的是这个,它禁用该高速缓存,将默认导航超时设置为0,并通过在第一个
page.waitForResponse()
分辨率中获取响应来访问响应。iswrvxsc2#
我不确定我是否完全理解了你的用例,我也无法重现你的错误,所以很难给出准确的建议。
一般来说,这里有一个简单的单例
browser
/page
组合管理器:重要的是确保在发生错误时关闭浏览器。否则,您的脚本可能会永远挂起,耗尽资源并创建僵尸进程。
下面是另一种更直接的方法,但不提供重新启动页面的方法:
希望所有的管理代码都可以隐藏在一个模块中。
其他方法是可能的。
如果您使用Express,请记住创建和销毁页面应该是相当轻量级的,并且您实际上不能与不同的客户端共享此单例页面。如果你真的不想创建新的页面,考虑一个页面池,路由处理程序可以为任务“租用”页面,然后将它们返回到池中。puppeteer-cluster可能是一个很好的选择。但我会避免过早的优化,我对你的实际用例进行了大量的推测。
注意
networkidle0
非常慢,所以如果你需要优化,我会先看看你的主线Puppeteer工作。