Кукловод - Ошибка протокола (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', но я надеюсь, что это неверно ... Что-то мне не хватает?


person LioRz    schedule 01.08.2018    source источник
comment
какие-нибудь выводы ...?   -  person Sajuuk    schedule 24.01.2019
comment
@Sajuuk К сожалению, не совсем ... но я посмотрел это видео и подумал, что потеряю кластер и попробуйте архитектуру PubSub, где сервер Puppeteer будет читать имена хостов, возвращать снимок экрана и переходить к следующему   -  person LioRz    schedule 29.01.2019
comment
Если вы недавно обновились до macOS Catalina, см. Эту проблему: github.com/GoogleChrome/puppeteer/issues/ 5020   -  person Flimm    schedule 21.10.2019


Ответы (6)


Что означает "Цель закрыта"

Когда вы запускаете браузер через puppeteer.launch, он запускает браузер и подключается к нему. Оттуда любая функция, которую вы выполняете в открытом браузере (например, page.goto), будет отправляться через протокол Chrome DevTools в браузер. В данном контексте цель означает вкладку.

Исключение Target closed выдается, когда вы пытаетесь запустить функцию, но цель (вкладка) уже закрыта.

Подобные сообщения об ошибках

Сообщение об ошибке было недавно изменено, чтобы дать более значимую информацию. Теперь он дает следующее сообщение:

Ошибка: ошибка протокола (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-cluster (отказ от ответственности: я автор) который обрабатывает такие случаи ошибок и позволяет вам повторить URL-адрес при возникновении ошибки. Он также может управлять пулом экземпляров браузера и упростить ваш код.

person Thomas Dondorf    schedule 23.08.2018
comment
Я получаю это почти для 95% запросов. - person Sajuuk; 24.01.2019
comment
произойдет ли это, если ресурс каким-то образом никогда не открывал страницу, как в случае сбоя инициализации? - person James Cat; 24.03.2020
comment
@JamesCat Да, это возможно, например, когда происходит сбой браузера во время инициализации. - person Thomas Dondorf; 24.03.2020
comment
@ThomasDondorf просто небольшое примечание для других, я решил это, проблема заключалась в том, что я запускал драматурга в сборке контейнера докеров из узла: у нас с Erbium не было всех необходимых зависимостей - person James Cat; 31.03.2020
comment
Я бы опубликовал новый ответ, но это правильно, просто не забудьте добавить await перед browser.close() - person Hlawuleka MAS; 05.05.2020
comment
Я получаю это со 100% запросов на определенные веб-страницы - person johnstaveley; 04.06.2020
comment
Вы также можете получить это, случайно исключив await из других команд, например page.keyboard.press('Enter') - person Raine Revere; 21.05.2021

У меня возникала одна и та же проблема каждый раз, когда я пытался запустить свой сценарий кукольника *. выше не помогло мне решить эту проблему.

Я заставил его работать, удалив и переустановив пакет кукловода:

npm remove puppeteer
npm i puppeteer

* Я столкнулся с этой проблемой только при установке параметра headless на 'false'

person Timo    schedule 27.07.2020

Для меня удаление '--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',
    ],
}
person Shaig Khaligli    schedule 07.04.2021
comment
30 июня 2021 года - это был для меня правильный ответ. - person Shawn Naquin; 01.07.2021

В 2021 году я получаю очень похожую следующую ошибку Error: Error pdf creationError: Protocol error (Target.setDiscoverTargets): Target closed., я решил ее, играя с разными аргументами, поэтому, если на вашем производственном сервере установлен флаг pipe:true в puppeteer.launch obj, это вызовет ошибки.

Также --disable-dev-shm-usage flag сделает свое дело

Приведенное ниже решение работает для меня:

const browser = await puppeteer.launch({
  headless: true,
  // pipe: true, <-- delete this property
  args: [
    '--no-sandbox',
    '--disable-dev-shm-usage', // <-- add this one
    ],
});
person Kurkov Igor    schedule 15.03.2021

Проверьте свой файл jest-puppeteer.config.js. Я сделал ошибку ниже

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

и после исправления, как показано ниже

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

все работало отлично !!!

person Shashank Shukla    schedule 27.06.2020

Я несколько раз попадал в этот поток, и типичный виновник - то, что я забыл 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

В этом случае это похоже на ошибку, которую нельзя пропустить, но в большем фрагменте кода, а обещание вложено или находится в состоянии, это легко не заметить.

Вы получите аналогичную ошибку, если забудете await page.click() или другой вызов обещания, например Error: Protocol error (Runtime.callFunctionOn): Target closed., который можно увидеть в вопросе UnhandledPromiseRejectionWarning: Ошибка: ошибка протокола (Runtime.callFunctionOn): цель закрыта. (Кукольник)

Это вклад в поток как канонический ресурс для ошибки и не может быть решением проблемы OP, хотя фундаментальное состояние гонки кажется вероятной причиной.

person ggorlen    schedule 02.07.2021