Puppeteer: 最後のページを閉じたときにブラウザを閉じると、エラーがスローされることがあります

作成日 2018年03月27日  ·  9コメント  ·  ソース: puppeteer/puppeteer

あなたの環境について教えてください:

  • Puppeteerバージョン:1.2.0
  • プラットフォーム/ OSバージョン:CentOS 7.4.1708
  • URL(該当する場合):N / A
  • Node.jsバージョン:v8.10.0

どの手順で問題が再現されますか?

const puppeteer = require('puppeteer');

const run = async () => {
  const browser = await puppeteer.launch();

  // close any open pages
  let pages = await browser.pages();
  await Promise.all(pages.map(async page => await page.close()));

  // handle a page being closed
  browser.on('targetdestroyed', async target => {
    const openPages = await browser.pages();
    console.log('Open pages:', openPages.length);
    if (openPages.length == 0) {
      console.log('Closing empty browser');
      await browser.close();
      console.log('Browser closed');
    }
  });

  pages = await browser.pages();
  for (let i = 0; i < 5; i++) {
    await browser.newPage();
  }

  pages = await browser.pages();
  pages.forEach((page) => page.close());
};

run();

期待される結果は何ですか?

Count: 0
Open pages: 4
Open pages: 3
Open pages: 2
Open pages: 1
Open pages: 0
Closing empty browser
Browser closed

代わりに何が起こりますか?

Open pages: 4
Open pages: 3
Open pages: 2
Open pages: 1
Open pages: 0
Closing empty browser
(node:29074) UnhandledPromiseRejectionWarning: Error: Protocol error (Target.closeTarget): Target closed.
    at Promise (node_modules/puppeteer/lib/Connection.js:86:56)
    at new Promise (<anonymous>)
    at Connection.send (node_modules/puppeteer/lib/Connection.js:85:12)
    at Page.close (node_modules/puppeteer/lib/Page.js:802:36)
    at pages.forEach (test.js:27:32)
    at Array.forEach (<anonymous>)
    at run (test.js:27:9)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
(node:29074) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:29074) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Browser closed

これはすべての実行で発生するわけではありませんが、頻繁に発生します。 私がここで何か悪いことをしている可能性は完全にあります!

bug chromium

全てのコメント9件

+1

少し簡単にデバッグしましたが、 page.close()メッセージが確認される前に(つまり、パペッティアが確認を処理する前に) targetDestroyedイベントが発生します。

私はそれを修正する方法を考えることができます-例えば、 targetDestroyedイベントを処理する前にTarget.closeTarget確認応答を待ちます。 それを適切に行う方法は別の質問です!

調査してくれた@MJPAに感謝します。

この問題は、関連するページが閉じられる前にbrowserがリストからターゲットを削除するという事実に起因します。

次の例では、 page.close()呼び出しが終了するまで、 browser.pages()が1ページをレポートすることを期待しています。

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const [page] = await browser.pages();
  browser.on('targetdestroyed', async () => console.log('Target destroyed. Pages count: ' + (await browser.pages()).length));
  console.log('closing page...');
  await page.close();
  console.log('page closed.');
  await browser.close();
})();

出力:

closing page...
Target destroyed. Pages count: 0
page closed.

@JoelEinbinderどう思いますか?

@MJPAと同じ問題

        await page.waitForSelector('aside.aside a[href="/checkout"]')
    .then(() => {
        logMessage('Checkout is available');
    })
    .catch(err => {
        log('Checkout not available');
        success = false;
        browser.close();
    });
    // click checkout button
    await Promise.all([
        page.click('aside.aside a[href="/checkout"]'),
        page.waitForNavigation()
    ]);

この場合、常に同じエラーが発生しました。

多分このパペッティアエラー。

少し似た実装でも同じエラーが発生しますが、 page closeイベントをリッスンします。 ドキュメントは「閉じる」の意味について過度に明示的ではありませんが、その実装によれば、 closeはページが閉じられる前に起動されるため、 targetdestroyedように次の例は機能しません。

const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();

// create 5 test pages
for(let i=0; i<5; i++){

  let page = await browser.newPage();

  // listen to page close event and close browser
  // when all get closed
  page.on('close', async () => {
    let pages = await browser.pages();
    if(!pages.length){
      // will not get here!
      browser.close();
    }
  });

}

let pages = await browser.pages();

pages.forEach(async (page) => await page.close());  

イベントはclosedではなくcloseと呼ばれるので、この_sortof_は理にかなっていると思います。 close() promiseコールバックで自分のclosedイベントを発行し、代わりにそれをリッスンすることで、これを回避しました。これは機能します。

page.close().then(() => {
  page.emit('closed');
});

話の教訓は、ブラウザがいつ安全に閉じることができるかを知る100%保証された方法がないようです(例外をキャッチする必要はありません)。 足りないものがあれば教えてください。

おそらく#3423に関連していますか?

ブラウザのすべてのタブを閉じてから新しいページを開こうとすると、これが表示されます

次の例では、 page.close()呼び出しが終了するまで、 browser.pages()が1ページをレポートすることを期待しています。

これにより、上記の例のコードが破損します。 最後に閉じられたページ、brower.pages()。lengthは1になり、ブラウザーは閉じられません。 そうすれば、ターゲットが破壊されたイベントが発生しなくなり、ブラウザは永久に存続します。

微妙ですが、これはユーザーエラーだと思います。 page.close呼び出しは、 browser.close()呼び出す前に待機する必要があります。

ユーザーが制御するページがない場合は、Promise.allのすべてのページを閉じてから、ブラウザーを閉じます。 targetdestroyedイベントを聞く必要はありません。 または、browser.closeを呼び出して、すべてのページを自動的に閉じます。

ユーザーが制御するページがある場合は、 targetdestroyedイベントをリッスンしますが、 page.closeを呼び出す必要はありません。

+1。
page.on("popup")ポップアップを閉じた後、このエラーが表示され始めました。 フレームコンテキスト内でポップアップが発生すると、「popup.close()」でポップアップを閉じることができず、パペッティアがタブを開いたままにすることがあるため、ブラウザ全体を閉じようとすると、「TargetClosed」エラーがスローされます。スクリプトが停止します。

Chrome73でpuppeteer1.14を使用しています(Chromiumは使用していません)
ブラウザを閉じるために使用するコード

async close_browser() {
    for (let page of await this.browser.pages()) {
      await page.close({
        "runBeforeUnload": true
      });
    }
   await this.browser.close();
}
このページは役に立ちましたか?
0 / 5 - 0 評価