45

场景:用于开发人员应用注册的 Web 表单,具有两部分工作流程。

第 1 页:填写开发者应用程序详细信息并单击按钮以创建应用程序 ID,该应用程序 ID 在新选项卡中打开...

第 2 页:应用程序 ID 页面。我需要从该页面复制 App ID,然后关闭选项卡并返回第 1 页并填写 App ID(从第 2 页保存),然后提交表单。

我了解基本用法 - 如何打开第 1 页并单击打开第 2 页的按钮 - 但是当第 2 页在新选项卡中打开时,我如何获得第 2 页的句柄?

例子:

const puppeteer = require('puppeteer');

(async() => {
    const browser = await puppeteer.launch({headless: false, executablePath: '/Applications/Google Chrome.app'});
    const page = await browser.newPage();

    // go to the new bot registration page
    await page.goto('https://register.example.com/new', {waitUntil: 'networkidle'});

    // fill in the form info
    const form = await page.$('new-app-form');

    await page.focus('#input-appName');
    await page.type('App name here');

    await page.focus('#input-appDescription');
    await page.type('short description of app here');

    await page.click('.get-appId'); //opens new tab with Page 2

    // handle Page 2
    // get appID from Page 2
    // close Page 2

    // go back to Page 1
    await page.focus('#input-appId');
    await page.type(appIdSavedFromPage2);

    // submit the form
    await form.evaluate(form => form.submit());

    browser.close();
})();

2017-10-25 更新

仍在寻找一个好的使用示例。

4

8 回答 8

20

两天前已经提交了一个新补丁,现在您可以使用browser.pages()当前浏览器访问所有页面。工作正常,昨天自己试过了:)

编辑:

如何获取作为“目标:_blank”链接打开的新页面的 JSON 值的示例。

const page = await browser.newPage();
await page.goto(url, {waitUntil: 'load'});

// click on a 'target:_blank' link
await page.click(someATag);

// get all the currently open pages as an array
let pages = await browser.pages();

// get the last element of the array (third in my case) and do some 
// hucus-pocus to get it as JSON...
const aHandle = await pages[3].evaluateHandle(() => document.body);

const resultHandle = await pages[3].evaluateHandle(body => 
  body.innerHTML, aHandle);

// get the JSON value of the page.
let jsonValue = await resultHandle.jsonValue();

// ...do something with JSON
于 2017-11-03T06:46:40.030 回答
14

这将在最新的 alpha 分支中为您工作:

const newPagePromise = new Promise(x => browser.once('targetcreated', target => x(target.page())));
await page.click('my-link');
// handle Page 2: you can access new page DOM through newPage object
const newPage = await newPagePromise;
await newPage.waitForSelector('#appid');
const appidHandle = await page.$('#appid');
const appID = await page.evaluate(element=> element.innerHTML, appidHandle );
newPage.close()
[...]
//back to page 1 interactions

通过将package.json依赖项设置为,确保使用最后一个 puppeteer 版本(来自 Github master 分支)

"dependencies": {
    "puppeteer": "git://github.com/GoogleChrome/puppeteer"
},

来源:JoelEinbinder @ https://github.com/GoogleChrome/puppeteer/issues/386#issuecomment-343059315

于 2017-10-27T21:44:52.257 回答
9

根据官方文档

browser.pages()

  • 返回:Promise,它解析为所有打开页面的数组。此处不会列出不可见的页面,例如。您可以使用.<Promise<Array<Page>>>"background_page"target.page()

浏览器内所有页面的数组。在多个浏览器上下文的情况下,该方法将返回一个数组,其中包含所有浏览器上下文中的所有页面。

示例用法:

let pages = await browser.pages();
await pages[0].evaluate(() => { /* ... */ });
await pages[1].evaluate(() => { /* ... */ });
await pages[2].evaluate(() => { /* ... */ });
于 2018-08-24T23:18:15.460 回答
4

理论上,您可以覆盖该window.open功能以始终在当前页面上打开“新标签”并通过历史记录导航。

您的工作流程将是:

  1. 重写window.open函数:

    await page.evaluateOnNewDocument(() => {
      window.open = (url) => {
        top.location = url
      }
    })
    
  2. 转到您的第一页并执行一些操作:

    await page.goto(PAGE1_URL)
    // ... do stuff on page 1
    
  3. 通过单击按钮导航到您的第二页并在那里执行一些操作:

    await page.click('#button_that_opens_page_2')
    await page.waitForNavigation()
    // ... do stuff on page 2, extract any info required on page 1
    // e.g. const handle = await page.evaluate(() => { ... })
    
  4. 返回您的第一页:

    await page.goBack()
    // or: await page.goto(PAGE1_URL)
    // ... do stuff on page 1, injecting info saved from page 2
    

显然,这种方法有其缺点,但我发现它极大地简化了多选项卡导航,如果您已经在多个选项卡上运行并行作业,这将特别有用。不幸的是,当前的 API 并不容易。

于 2017-11-17T19:50:55.790 回答
3

如果它是由属性引起的,您可以消除切换页面的需要target="_blank"- 通过设置target="_self"

例子:

element = page.$(selector)

await page.evaluateHandle((el) => {
        el.target = '_self';
 }, element)

element.click()
于 2017-12-30T23:21:30.830 回答
2

如果您的点击操作正在发出页面加载,那么任何后续正在运行的脚本都会有效丢失。要解决此问题,您需要触发操作(在本例中为单击),但不需要触发 await。相反,等待页面加载:

page.click('.get-appId');
await page.waitForNavigation();

这将允许您的脚本在继续执行进一步操作之前有效地等待下一个页面加载事件。

于 2018-05-30T07:27:32.773 回答
1

您目前不能 - 关注https://github.com/GoogleChrome/puppeteer/issues/386以了解何时将能力添加到 puppeteer(希望很快)

于 2017-08-22T07:40:10.197 回答
0

看起来有一个简单的“page.popup”事件

与“弹出”窗口对应的页面在页面打开新选项卡或窗口时发出。

const [popup] = await Promise.all([
  new Promise(resolve => page.once('popup', resolve)),
  page.click('a[target=_blank]'),
]);
const [popup] = await Promise.all([
  new Promise(resolve => page.once('popup', resolve)),
  page.evaluate(() => window.open('https://example.com')),
]);

归功于这个 github 问题,以便更轻松地“创建目标”

于 2019-10-28T18:13:40.183 回答