Puppeteer检测新标签页的打开并获取页面对象。

16

我的Web应用程序在某些条件下会打开一个新选项卡。但是当我尝试获取所有选项卡(await browser.pages())时,我只能得到一个,即初始页面。

我该如何在我的代码中获取新页面的对象?

当您不使用await browser.newPage()通过puppeteer创建新选项卡时,就会发生这种情况,而是像这样做:

await (await browser.pages())[0].evaluate(() => {
    window.open('http://www.example.com', '_blank');
});

browser.pages() 的响应中,该页面将不可用。

3个回答

51

如果新页面是通过在原始页面上单击链接打开的,则此代码将捕获新选项卡中的新页面。

//save target of original page to know that this was the opener:     
const pageTarget = page.target();
//execute click on first tab that triggers opening of new tab:
await page.click('#selector');
//check that the first page opened this new page:
const newTarget = await browser.waitForTarget(target => target.opener() === pageTarget);
//get the new page object:
const newPage = await newTarget.page();

1
我很高兴这个答案比我的好多了。看起来新版本为此引入了一些不错的API。 - Md. Abu Taher
如果您打开多个选项卡会怎样呢?您将始终获得相同的第一页。 - user2715109

7

如果不知道应用程序何时打开新标签页,就很难解决问题。而我使用这个功能非常顺畅。以下是演示如何使用它的代码。请阅读注释以了解步骤。

更新:

window.open() 不返回 promise,因此 browser.pages() 的执行速度比浏览器创建和报告事件的速度更快。我们可以使用 targetcreated 事件来确定是否创建了任何新标签页。

browser.on('targetcreated', function(){
    console.log('New Tab Created');
})

如果你稍等片刻或者返回一个promise,你会在browser.pages()计数中看到它的报告。
await tabOne.evaluate(() => {
    window.open('http://www.example.com', '_blank');
  });
await tabOne.waitFor(2000); // await for a while
console.log("current page count ", (await browser.pages()).length); // 3

这是最终代码。
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();

  browser.on('targetcreated', function(){
    console.log('New Tab Created');
  })

  // get current tab count
  console.log("current page count ", (await browser.pages()).length); // 3

  // create a new tab
  await browser.newPage();
  // lets see if tab increased
  console.log("current page count ", (await browser.pages()).length); // 3

  // use destructuring for easier usage
  const [tabOne, tabTwo] = (await browser.pages());

  // use the tabs aka Page objects properly
  await tabOne.goto('https://example.com');
  console.log("Tab One Title ",await tabOne.title()); // Example Domain

  // use the tabs aka Page objects properly
  await tabTwo.goto('https://example.com');
  console.log("Tab Two Title ",await tabTwo.title()); // Example Domain

  await tabOne.evaluate(() => {
    window.open('http://www.example.com', '_blank');
  });
  await tabOne.waitFor(2000); // wait for a while
  console.log("current page count ", (await browser.pages()).length); // 3

  // close the browser
  await browser.close();
})();

如果你运行它,你将按以下顺序获得结果。
/*
current page count  1
New Tab Created
current page count  2
Tab One Title  Example Domain
Tab Two Title  Example Domain
New Tab Created
current page count  3
*/

这是当你从你的代码中创建一个选项卡时。但在我的情况下,选项卡是自动创建在页面内的。 - Konstantin Bodnia
看一下。如果你使用以下代码替换 await browser.newPage():await (await browser.pages())[0].evaluate(() => {window.open('http://www.example.com', '_blank');});那么这个页面将不会在 browser.pages() 的返回结果中可用。 - Konstantin Bodnia
它确实有报告。只是window.open没有返回一个promise,所以browser.pages()还不知道它。更新答案,提供更多信息。 - Md. Abu Taher
@Md.AbuTaher 我的代码打开了一个新窗口而不是进入您的if语句以打印“新标签页已创建”。您能否请参考:https://dev59.com/i6zla4cB1Zd3GeqPFfga? - ziad.ali

0

除了这种(完全没问题的!)方法之外,另一种方法是将浏览器的"targetcreated"事件转换为Promise,具体描述在Puppeteer issue #386的评论中:

const puppeteer = require("puppeteer");
const { once } = require('events');

(async () => {
  const html = `<a href="https://news.ycombinator.com" target="_blank">click</a>`;
  const browser = await puppeteer.launch({headless: false});
  const [page] = await browser.pages(); 
  await page.setContent(html);
  await page.waitForSelector("a", {visible: true});
  console.log((await browser.pages()).length); // => 1

  const newPagePromise = once("targetcreated", browser).then(x => x.page());
  await page.click("a");
  const newPage = await newPagePromise;

  console.log((await browser.pages()).length); // => 2
  console.log((await newPage.content()));
  await browser.close();
})();

另一种写法是使用 Promise.all 和解构赋值来获取结果数组的第一个元素:

const [newPage] = await Promise.all([
  once(browser, "targetcreated").then(x => x.page),
  page.click("a")
]);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接