木偶-协议错误(Page.navigate):目标已关闭

时间:2018-08-01 08:54:59

标签: node.js web-scraping puppeteer google-chrome-headless node-cluster

正如您在下面的示例代码中看到的那样,我将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 });
            });
    });
}

一些解释:

  1. 每次请求到达时,工作人员都会对其进行处理并最终杀死自己
  2. 每个工作程序都使用一个页面创建一个新的浏览器实例,如果页面加载时间超过60秒,它将尝试以120秒的超时尝试重新加载它(在同一页面中,因为可能已经加载了某些资源)< / li>
  3. 完成页面后,浏览器将关闭

我的问题是某些合法域会收到我无法解释的错误:

Error: Protocol error (Page.navigate): Target closed.
Error: Protocol error (Runtime.callFunctionOn): Session closed. Most likely the page has been closed.

我读到一些git问题(我现在找不到),它可能在页面重定向并在开始时添加“ www”时发生,但是我希望它是错误的... 有什么我想念的吗?

6 个答案:

答案 0 :(得分:4)

每次尝试运行我的操纵up脚本*时,我都遇到相同的问题。 above不能为我解决此问题。

我通过删除并重新安装puppeteer软件包来使其正常工作:

npm remove puppeteer
npm i puppeteer

*我仅在将headless选项设置为“ false”时遇到此问题

答案 1 :(得分:2)

“关闭目标”的含义

通过puppeteer.launch启动浏览器时,它将启动浏览器并连接到该浏览器。从那里开始,您在打开的浏览器中执行的所有功能(例如page.goto)都将通过Chrome DevTools Protocol发送到浏览器。在这种情况下,目标意味着一个标签。

尝试运行函数时会抛出目标已关闭异常,但目标(选项卡)已关闭。

类似的错误消息

错误消息最近已更改为提供更多有意义的信息。现在它显示以下消息:

  

错误:协议错误(Target.activateTarget):会话已关闭。页面很可能已关闭。


为什么会发生

发生这种情况有多种原因。

  • 您使用了已经关闭的资源

    很可能您看到此消息,因为您关闭了选项卡/浏览器,并且仍在尝试使用资源。举一个简单的例子:

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    
    await browser.close();
    await page.goto('http://www.google.com');
    

    在这种情况下,浏览器已关闭,此后,调用page.goto导致错误消息。大多数时候,情况不会那么明显。可能是错误处理程序在清理任务期间已经关闭了页面,而脚本仍在爬网。

  • 浏览器崩溃或无法初始化

    每隔几百个请求,我也会遇到一次。在操纵up存储库中也有一个issue about this。当您使用大量内存或CPU电源时,情况似乎确实如此。也许您产生了很多浏览器?在这种情况下,浏览器可能会崩溃或断开连接。

    我没有找到解决该问题的“灵丹妙药”解决方案。但是您可能想检查一下puppeteer-cluster库(免责声明:我是作者),该库可以处理此类错误情况,让您在发生错误时重试URL。它还可以管理一组浏览器实例,并且还可以简化您的代码。

答案 2 :(得分:2)

在 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
    ],
});

答案 3 :(得分:2)

对我来说,从 '--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',
    ],
}

答案 4 :(得分:0)

检查您的jest-puppeteer.config.js文件。 我犯了以下错误

module.exports = {
    launch: {
        headless: false,
        browserContext: "default",
    },
};

并按照以下说明进行纠正

module.exports = {
    launch: {
        headless: false
    },
    browserContext: "default",
};

一切正常!!

答案 5 :(得分:0)

我已经在这个线程上结束了几次,典型的罪魁祸首是我忘记了 await 一个返回承诺的 Puppeteer page 调用,导致了竞争条件。

以下是其外观的最小示例:

const puppeteer = require("puppeteer");

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  page.goto("https://www.stackoverflow.com"); // whoops, forgot await!
})()
  .catch(err => console.error(err))
  .finally(async () => await browser.close())
;

输出为:

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

在这种情况下,这似乎是一个不容错过的错误,但在较大的代码块和 promise 嵌套或条件中,很容易被忽视。

忘记await一个page.click()或其他promise调用,例如Error: Protocol error (Runtime.callFunctionOn): Target closed.,你会得到一个类似的错误,这可以在问题UnhandledPromiseRejectionWarning: Error: Protocol error (Runtime.callFunctionOn): Target closed. (Puppeteer)中看到

这是对线程作为错误规范资源的贡献,可能不是 OP 问题的解决方案,尽管基本的竞争条件似乎是一个可能的原因。< /sub>