正如Puppeter文档中所述;对话框";事件如下:
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键来接受确认对话框,或者在我想关闭确认对话框时转到下一页。
有什么解决方案可以在Puppeter这样的循环中使用对话框事件吗?
======
更新
======
//@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());
这个答案是Puppeter不拾取对话框的变体。快速总结一下这个答案:.on
处理程序可以很容易地将等待它们集成到控制流中,而不会出现混乱的回调。OP代码中似乎丢失了一个重要的细微差别,即如果您只等待一次,请使用.once
而不是.on
,或者使用.off
来删除侦听器。解决后,侦听器就会变得陈旧。
在这种情况下,假设您有一组指向显示确认对话框的页面的URL(或者您插入自己的确认对话框(,对于每个URL,您希望为对话框添加一个处理程序,以便根据条件接受或取消它。您可能还想从对话框中收集消息,如下所示。
这里有一个简单的例子:
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
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