Lio*_*oRz 5 node.js web-scraping node-cluster google-chrome-headless puppeteer
如您在下面的示例代码中所看到的,我将Puppeteer与Node中的一组工作人员一起使用,以通过给定的URL运行多个网站截图请求:
const cluster = require('cluster');
const express = require('express');
const bodyParser = require('body-parser');
const puppeteer = require('puppeteer');
async function getScreenshot(domain) {
let screenshot;
const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'] });
const page = await browser.newPage();
try {
await page.goto('http://' + domain + '/', { timeout: 60000, waitUntil: 'networkidle2' });
} catch (error) {
try {
await page.goto('http://' + domain + '/', { timeout: 120000, waitUntil: 'networkidle2' });
screenshot = await page.screenshot({ type: 'png', encoding: 'base64' });
} catch (error) {
console.error('Connecting to: ' + domain + ' failed due to: ' + error);
}
await page.close();
await browser.close();
return screenshot;
}
if (cluster.isMaster) {
const numOfWorkers = require('os').cpus().length;
for (let worker = 0; worker < numOfWorkers; worker++) {
cluster.fork();
}
cluster.on('exit', function (worker, code, signal) {
console.debug('Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal);
Cluster.fork();
});
cluster.on('message', function (handler, msg) {
console.debug('Worker: ' + handler.process.pid + ' has finished working on ' + msg.domain + '. Exiting...');
if (Cluster.workers[handler.id]) {
Cluster.workers[handler.id].kill('SIGTERM');
}
});
} else {
const app = express();
app.use(bodyParser.json());
app.listen(80, function() {
console.debug('Worker ' + process.pid + ' is listening to incoming messages');
});
app.post('/screenshot', (req, res) => {
const domain = req.body.domain;
getScreenshot(domain)
.then((screenshot) =>
try {
process.send({ domain: domain });
} catch (error) {
console.error('Error while exiting worker ' + process.pid + ' due to: ' + error);
}
res.status(200).json({ screenshot: screenshot });
})
.catch((error) => {
try {
process.send({ domain: domain });
} catch (error) {
console.error('Error while exiting worker ' + process.pid + ' due to: ' + error);
}
res.status(500).json({ error: error });
});
});
}
Run Code Online (Sandbox Code Playgroud)
一些解释:
我的问题是某些合法域会收到我无法解释的错误:
Error: Protocol error (Page.navigate): Target closed.
Run Code Online (Sandbox Code Playgroud)
Error: Protocol error (Runtime.callFunctionOn): Session closed. Most likely the page has been closed.
Run Code Online (Sandbox Code Playgroud)
我读到某个git问题(我现在找不到),它会在页面重定向并在开始时添加“ www”时发生,但是我希望它是错误的……我缺少什么吗?
Sha*_*gli 18
对我来说,解决'--single-process'了args这个问题。
puppeteerOptions: {
headless: true,
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-setuid-sandbox',
'--no-first-run',
'--no-sandbox',
'--no-zygote',
'--deterministic-fetch',
'--disable-features=IsolateOrigins',
'--disable-site-isolation-trials',
// '--single-process',
],
}
Run Code Online (Sandbox Code Playgroud)
ggo*_*len 12
我已经在这个线程中遇到过几次,典型的罪魁祸首是我忘记了返回承诺的awaitPuppeteer调用,从而导致了竞争条件。page
下面是一个最小的示例:
const puppeteer = require("puppeteer");
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
page.goto("https://www.stackoverflow.com"); // whoops, forgot await!
})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
Run Code Online (Sandbox Code Playgroud)
输出是:
C:\Users\foo\Desktop\puppeteer-playground\node_modules\puppeteer\lib\cjs\puppeteer\common\Connection.js:217
this._callbacks.set(id, { resolve, reject, error: new Error(), method });
^
Error: Protocol error (Page.navigate): Target closed.
at C:\Users\foo\Desktop\puppeteer-playground\node_modules\puppeteer\lib\cjs\puppeteer\common\Connection.js:217:63
Run Code Online (Sandbox Code Playgroud)
在这种情况下,这似乎是一个不容忽视的错误,但在较大的代码块中并且承诺是嵌套的或在条件中,它很容易被忽视。
await如果忘记调用某个Promise 或其他 Promise,您会收到类似的错误page.click(),例如 ,Error: Protocol error (Runtime.callFunctionOn): Target closed.这可以在问题UnhandledPromiseRejectionWarning: Error: Protocol error (Runtime.callFunctionOn): Target returned 中看到。(傀儡师)
这是对线程作为错误的规范资源的贡献,并且可能不是OP问题的解决方案,尽管基本的竞争条件似乎是一个可能的原因。
每次尝试运行我的 puppeteer 脚本时,我都遇到了同样的问题*。在上面并没有解决我这个问题。
我通过删除并重新安装 puppeteer 包来让它工作:
npm remove puppeteer
npm i puppeteer
Run Code Online (Sandbox Code Playgroud)
*我只在将 headless 选项设置为 'false` 时遇到过这个问题
当您通过puppeteer.launch它启动浏览器时,将启动浏览器并连接到它。从那里开始,您在打开的浏览器中执行的所有功能(例如page.goto)都将通过Chrome DevTools协议发送到浏览器。在此上下文中,目标表示选项卡。
该目标闭合,当你正在尝试运行的功能异常被抛出,但目标(标签)已经关闭。
该错误消息最近已更改为提供更多有意义的信息。现在,它显示以下消息:
错误:协议错误(Target.activateTarget):会话关闭。该页面很可能已关闭。
发生这种情况的原因有多种。
您使用了已经关闭的资源
您最有可能看到此消息,因为您关闭了选项卡/浏览器,并且仍在尝试使用资源。举一个简单的例子:
const browser = await puppeteer.launch();
const page = await browser.newPage();
await browser.close();
await page.goto('http://www.google.com');
Run Code Online (Sandbox Code Playgroud)
在这种情况下,浏览器已关闭,此后,page.goto调用,导致错误消息。大多数时候,情况不会那么明显。在脚本仍在爬网的过程中,也许错误处理程序已在清理任务期间关闭了页面。
浏览器崩溃或无法初始化
每隔几百个请求,我也会遇到一次。在操纵up存储库中也存在与此相关的问题。当您使用大量内存或CPU电源时,情况似乎确实如此。也许您产生了很多浏览器?在这些情况下,浏览器可能会崩溃或断开连接。
我没有找到解决此问题的“灵丹妙药”解决方案。但是您可能想查看库puppeteer-cluster(免责声明:我是作者),该库处理此类错误情况,让您在发生错误时重试URL。它还可以管理浏览器实例池,并且还可以简化代码。
在 2021 年,我收到了非常相似的以下错误Error: Error pdf creationError: Protocol error (Target.setDiscoverTargets): Target closed.,我通过使用不同的参数解决了它,因此如果您的生产服务器pipe:true在 obj 中有一个标志puppeteer.launch,它将产生错误。
也可以--disable-dev-shm-usage标记做伎俩
下面的解决方案对我有用:
const browser = await puppeteer.launch({
headless: true,
// pipe: true, <-- delete this property
args: [
'--no-sandbox',
'--disable-dev-shm-usage', // <-- add this one
],
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5819 次 |
| 最近记录: |