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

新手上路,请多包涵

正如您在下面的示例代码中看到的那样,我使用 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 秒
  3. 完成后,页面和浏览器都将关闭

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

 Error: Protocol error (Page.navigate): Target closed.

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

我在一些 git 问题(我现在找不到)中读到,当页面重定向并在开头添加“www”时可能会发生这种情况,但我希望它是错误的……我有什么遗漏吗?

原文由 LioRz 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.7k
2 个回答

“目标关闭”是什么意思

当您通过 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');

在这种情况下,浏览器已关闭,然后调用 page.goto 导致错误消息。大多数时候,它不会那么明显。也许一个错误处理程序已经在清理任务期间关闭了页面,而您的脚本仍在爬行。

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

我每隔几百个请求也会遇到这种情况。 puppeteer 存储库中也有一个 关于此 的问题。当您使用大量内存或 CPU 能力时,似乎就是这种情况。也许你正在催生很多浏览器?在这些情况下,浏览器可能会崩溃或断开连接。

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

原文由 Thomas Dondorf 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

原文由 Shaig Khaligli 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题