【问题标题】:Puppeteer (Cluster) closing page when I interact with it当我与之交互时,Puppeteer(Cluster)关闭页​​面
【发布时间】:2019-09-24 20:41:52
【问题描述】:

在 NodeJS v10.xx 环境中,当尝试从一些 HTML 代码创建 PDF 页面时,每次尝试使用它(setCacheEnabled、setRequestInterception 等)时都会遇到关闭页面问题:

async (page, data) => {
  try {
    const {options, urlOrHtml} = data;
    const finalOptions = { ...config.puppeteerOptions, ...options };

    // Set caching flag (if provided)
    const cache = finalOptions.cache;
    if (cache != undefined) {
      delete finalOptions.cache;
      await page.setCacheEnabled(cache); //THIS LINE IS CAUSING THE PAGE TO BE CLOSED
    }

    // Setup timeout option (if provided)
    let requestOptions = {};
    const timeout = finalOptions.timeout;
    if (timeout != undefined) {
      delete finalOptions.timeout;
      requestOptions.timeout = timeout;
    }

    requestOptions.waitUntil = 'networkidle0';
    if (urlOrHtml.match(/^http/i)) {
      await page.setRequestInterception(true); //THIS LINE IS CAUSING ERROR DUE TO THE PAGE BEING ALREADY CLOSED
      page.once('request', request => {

        if(finalOptions.method === "POST" && finalOptions.payload !== undefined) {
          request.continue({method: 'POST', postData: JSON.stringify(finalOptions.payload)});
        }
      });

      // Request is for a URL, so request it
      await page.goto(urlOrHtml, requestOptions);
    }

    return await page.pdf(finalOptions);
  } catch (err) {
    logger.info(err);
  }
};

我在某处读到此问题可能是由于缺少一些等待而引起的,但这看起来不像我的情况。

我没有直接使用 puppeteer,而是这个库在其上创建集群并处理进程:

https://github.com/thomasdondorf/puppeteer-cluster

【问题讨论】:

  • 总是很高兴听到人们在使用图书馆 ????很高兴你解决了这个问题!希望您不介意我添加了另一个答案,然后我将使用此线程链接到是否有人有相同的问题。

标签: node.js async-await puppeteer puppeteer-cluster


【解决方案1】:

您已经给出了解决方案,但由于这是图书馆的常见问题(我是作者?)我想提供更多见解。

任务功能的工作原理

当作业排队并准备好执行时,puppeteer-cluster 将创建一个页面并使用创建的page 对象和排队数据调用任务函数(给cluster.task)。然后集群会一直等到 Promise 完成(完成或拒绝),然后关闭页面并执行队列中的下一个作业。

由于异步函数隐式创建了一个 Promise,这意味着一旦给 cluster.task 函数的异步函数完成,页面就会关闭。确定该页面将来是否会被使用并不是什么神奇的事情。

等待异步事件

下面是一个有一个常见错误的代码示例。用户可能希望在关闭页面之前等待外部事件,如下面的(不工作)示例所示:

无效 (!) 代码示例:

await cluster.task(async ({ page, data }) => {
    await page.goto('...');
    setTimeout(() => { // user is waiting for an asynchronous event
        await page.evaluate(/* ... */); // Will throw an error as the page is already closed
    }, 1000);
});

在这段代码中,页面在异步函数执行之前已经关闭。正确的做法是返回一个 Promise。

工作代码示例:

await cluster.task(async ({ page, data }) => {
    await page.goto('...');

    // will wait until the Promise resolves
    await new Promise(resolve => {
        setTimeout(() => { // user is waiting for an asynchronous event
            try {
                await page.evalute(/* ... */);
                resolve();
            } catch (err) {
                // handle error
            }
        }, 1000);
    });
});

在此代码示例中,任务函数会一直等待,直到内部 Promise 被解析,直到它解析函数。这将保持页面打开,直到异步函数调用resolve。此外,代码使用了try..catch 块,因为库无法捕获异步代码块内引发的事件。

【讨论】:

【解决方案2】:

我明白了。

我确实忘记了对我发布的函数的调用的等待。

该调用位于我用于创建集群实例的另一个文件中:

async function createCluster() {
  //We will protect our app with a Cluster that handles all the processes running in our headless browser
  const cluster = await Cluster.launch({
    concurrency: Cluster[config.cluster.concurrencyModel],
    maxConcurrency: config.cluster.maxConcurrency
  });

  // Event handler to be called in case of problems
  cluster.on('taskerror', (err, data) => {
    console.log(`Error on cluster task... ${data}: ${err.message}`);
  });

  // Incoming task for the cluster to handle
  await cluster.task(async ({ page, data }) => {
    main.postController(page, data); // <-- I WAS MISSING A return await HERE
  });

  return cluster;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    • 2016-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多