如何在 Puppeteer 循环中使用 page.on("dialog"?
How to use page.on("dialog" in a loop with Puppeteer?
正如 Puppeteer 文档中所述,“对话”事件的基本用法如下:
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());
});
这种方法的问题是我无法访问负责处理确认对话框 returns 的 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());
此答案是 的变体。该答案的快速总结:可以承诺 .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
正如 Puppeteer 文档中所述,“对话”事件的基本用法如下:
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());
});
这种方法的问题是我无法访问负责处理确认对话框 returns 的 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());
此答案是 .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