如何使用page.on("dialog"与Puppeteer循环使用?



正如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

最新更新