あなたの環境について教えてください:
どの手順で問題が再現されますか?
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
これはすべての実行で発生するわけではありませんが、頻繁に発生します。 私がここで何か悪いことをしている可能性は完全にあります!
+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();
}