正如Puppeteer文档中所述,“dialog”事件的基本用法如下:
page.on('dialog', async (dialog) => {
await dialog.dismiss()
// or await dialog.accept()
})
我想循环浏览 URL 列表,每个 URL 都会触发一个确认对话框。 但我想根据页面内容接受或关闭对话框。
我想知道是否可以?
当我在循环中使用它时,出现错误:“无法关闭已处理的对话框!”
for (let url in urls) {
if (condition) {
page.on("dialog", async (dialog) => {
await dialog.accept();
});
} else {
page.on("dialog", async (dialog) => {
await dialog.dismiss();
});
}
}
我在每个循环上添加一个侦听器,所以我收到错误。
但是当我将“对话框”侦听器移出循环时,我收到“对话框未定义”错误。
page.on("dialog", async (dialog) => {
for (let url in urls) {
if (condition) {
await dialog.accept();
} else {
await dialog.dismiss();
}
}
});
我尝试制作自定义事件侦听器。
await page.exposeFunction("test", async (e) => {
// But I don't know how to dismiss or accept the confirm dialog here.
});
await page.evaluate(() => {
window.addEventListener("confirm", window.test());
});
这种方法的问题是我无法访问负责处理确认对话框返回的
handleJavaScriptDialog
:
https://pub.dev/documentation/puppeteer/latest/puppeteer/Dialog/dismiss.html
到目前为止,我认为唯一的解决方案是模拟按 Enter 键来接受确认对话框,或者当我想关闭确认对话框时转到下一页。
是否有任何解决方案可以在像这样的 Puppeteer 循环中使用对话框事件?
======
更新
======
//@ggorlen 的示例
for (let url in urls) {
await page.goto(url);
const dialogDismissed = new Promise((resolve, reject) => {
const handler = async (dialog) => {
await dialog.dismiss();
resolve(dialog.message());
};
page.on("dialog", handler);
});
const dialogAccepted = new Promise((resolve, reject) => {
const handler = async (dialog) => {
await dialog.accept();
resolve(dialog.message());
};
page.on("dialog", handler);
});
await page.evaluate(() => window.confirm("Yes or No?"));
if (condition) {
//want to accept
//how to handle the dialog promise here?
} else {
//want to dismiss
//how to handle the dialog promise here?
}
}
======
更新2
======
//基于@ggorlen的回答,但不承诺处理程序
const puppeteer = require("puppeteer");
let browser;
(async () => {
const html = `<html><body><script>
document.write(confirm("yes or no?") ? "confirmed" : "rejected");
</script></body></html>`;
browser = await puppeteer.launch({
headless: true,
});
const [page] = await browser.pages();
const urls = ["just", "a", "demo", "replace", "this"];
for (const url of urls) {
const someCondition = Math.random() < 0.5; // for example
//This bloc is in question.
//Is there a need to promisify?
page.once("dialog", async (dialog) => {
console.log(dialog.message());
await (someCondition ? dialog.accept() : dialog.dismiss());
});
//await page.goto(url, {waitUntil: "networkidle0"});
await page.setContent(html);
console.log(await page.$eval("body", (el) => el.innerText));
}
})()
.catch((err) => console.error(err))
.finally(() => browser?.close());
这个答案是 Puppeteer 没有拾取对话框的变体。对该答案的快速总结:
.on
处理程序可以被承诺,以便轻松地将等待它们集成到控制流中,而无需混乱的回调。 OP 代码中似乎丢失的一个重要细微差别是,如果您只等待一次,请使用 .once
而不是 .on
,或者使用 .off
删除侦听器。解决后,监听器就变得陈旧了。
在这种情况下,假设您有一堆显示确认对话框的页面 URL(或者您注入自己的确认对话框),并且对于每个 URL,您希望为该对话框添加一个处理程序,以允许您接受或关闭它基于一个条件。您可能还想从对话框中收集消息,如下所示。
这是一个简单的例子:
const puppeteer = require("puppeteer"); // ^21.4.1
const html = `<html><body><script>
document.write(confirm("yes or no?") ? "confirmed" : "rejected");
</script></body></html>`;
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
const urls = ["just", "a", "demo", "replace", "this"];
for (const url of urls) {
const someCondition = Math.random() < 0.5; // for example
const dialogHandled = new Promise((resolve, reject) => {
const handler = async dialog => {
await (someCondition ? dialog.accept() : dialog.dismiss());
resolve(dialog.message());
};
page.once("dialog", handler);
});
//await page.goto(url, {waitUntil: "networkidle0"});
await page.setContent(html); // for demonstration
const msg = await dialogHandled;
console.log(msg, await page.$eval("body", el => el.innerText));
}
})()
.catch(err => console.error(err))
.finally(() => browser?.close());
示例运行类似于:
yes or no? confirmed
yes or no? confirmed
yes or no? confirmed
yes or no? rejected
yes or no? rejected