如何在 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 的 handleJavaScriptDialoghttps://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