は一般的に、あなたは「[編]そのタスクを完了」しているtempPage
まで実行を一時停止するawait tempPage.click("a.orange")
に頼ることはできません。同期的に実行されるスーパーシンプルコードの場合、動作する可能性があります。しかし、一般的に、あなたはそれに頼ることはできません。
クリックがAjax操作をトリガしたり、CSSアニメーションを開始したり、すぐに計算できない計算を開始したり、新しいページなどを開くと、待機中の結果は非同期であり、.click
メソッドは、この非同期操作が完了するのを待機しません。
あなたは何ができますか?場合によっては、ページ上で実行されているコードにフックして、あなたにとって重要なイベントを待つことができます。例えば、Ajax操作が完了するのを待っていて、ページ上のコードがjQueryを使用している場合は、操作が完了したことを検出するのにajaxComplete
を使用することができます。操作が完了したときに検出するイベント・システムに接続できない場合は、操作が完了したという証拠を待つためにページをポーリングする必要があります。
const puppeteer = require('puppeteer');
function getResults(page) {
return page.evaluate(() => ({
clicked: window.clicked,
asynchronousResponse: window.asynchronousResponse,
}));
}
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.goto("https://example.com");
// We add a button to the page that will click later.
await page.evaluate(() => {
const button = document.createElement("button");
button.id = "myButton";
button.textContent = "My Button";
document.body.appendChild(button);
window.clicked = 0;
window.asynchronousResponse = 0;
button.addEventListener("click",() => {
// Synchronous operation
window.clicked++;
// Asynchronous operation.
setTimeout(() => {
window.asynchronousResponse++;
}, 1000);
});
});
console.log("before clicks", await getResults(page));
const button = await page.$("#myButton");
await button.click();
await button.click();
console.log("after clicks", await getResults(page));
await page.waitForFunction(() => window.asynchronousResponse === 2);
console.log("after wait", await getResults(page));
await browser.close();
});
setTimeout
コードは、クリックによって開始された非同期操作のいずれかの種類をシミュレートします:ここで
は、問題を示し例です。このコードを実行すると
、あなたがコンソールに表示されます:
before click { clicked: 0, asynchronousResponse: 0 }
after click { clicked: 2, asynchronousResponse: 0 }
after wait { clicked: 2, asynchronousResponse: 2 }
あなたはclicked
がすぐに2回のクリックによって2回インクリメントされていることがわかります。ただし、asynchronousResponse
がインクリメントされるまでにはしばらく時間がかかります。ステートメントawait page.waitForFunction(() => window.asynchronousResponse === 2)
は、待機中の状態が実現するまでページをポーリングします。
ボタンでタブが閉じていることをコメントに記載しました。開閉タブは非同期操作です。ここでは例です:私は、上記を実行すると
puppeteer.launch().then(async browser => {
let pages = await browser.pages();
console.log("number of pages", pages.length);
const page = pages[0];
await page.goto("https://example.com");
await page.evaluate(() => {
window.open("https://example.com");
});
do {
pages = await browser.pages();
// For whatever reason, I need to have this here otherwise
// browser.pages() always returns the same value. And the loop
// never terminates.
await page.evaluate(() => {});
console.log("number of pages after evaluating open", pages.length);
} while (pages.length === 1);
let tempPage = pages[pages.length - 1];
// Add a button that will close the page when we click it.
tempPage.evaluate(() => {
const button = document.createElement("button");
button.id = "myButton";
button.textContent = "My Button";
document.body.appendChild(button);
window.clicked = 0;
window.asynchronousResponse = 0;
button.addEventListener("click",() => {
window.close();
});
});
const button = await tempPage.$("#myButton");
await button.click();
do {
pages = await browser.pages();
// For whatever reason, I need to have this here otherwise
// browser.pages() always returns the same value. And the loop
// never terminates.
await page.evaluate(() => {});
console.log("number of pages after click", pages.length);
} while (pages.length > 1);
await browser.close();
});
、私が取得:
number of pages 1
number of pages after evaluating open 1
number of pages after evaluating open 1
number of pages after evaluating open 2
number of pages after click 2
number of pages after click 1
あなたがwindow.open()
とwindow.close()
が検出可能な効果を持っている前に、それは少し時間がかかる見ることができます。
を私はawait
が、私はそれが同期に非同期機能をオン言わないだろう同期1
に非同期関数を回したものを基本的に思った:あなたも書いたあなたのコメントで
もの。現在のコードは、非同期操作の約束が解決または拒否されるのを待ちます。しかし、ここで問題となっている重要な点は、JavaScriptコードを実行する2つの仮想マシンがあることです。つまり、puppeteer
を実行するノードとブラウザを制御するスクリプトがあり、独自のJavaScript仮想マシンを持つブラウザ自体があります。 ノード側で使用するawait
は、ノードコードのみに影響します。これは、ブラウザで実行されるコードには影響しません。
await page.evaluate(() => { some code; })
のようなものを見ると混乱することがあります。すべてが同じもので、すべてが同じ仮想マシンで実行されているように見えますが、そうではありません。 puppeteer
は、渡されたパラメータを.evaluate
にしてシリアル化し、ブラウザに送信して実行します。 const button = ...
の後に、上記のスクリプトにawait page.evaluate(() => { button.click(); });
のようなものを追加してみてください。このような何か:スクリプトで
const button = await tempPage.$("#myButton");
await button.click();
await page.evaluate(() => { button.click(); });
、button
はpage.evaluate
前に定義されていますが、button
は、ブラウザ側で定義されていないため、あなたはReferenceError
ときpage.evaluate
ランを買ってあげます!