node.js Puppeteer:如何处理多个标签?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45806684/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Puppeteer: How to handle multiple tabs?
提问by nilsw
Scenario: Web form for developer app registration with two part workflow.
场景:用于开发者应用程序注册的 Web 表单,包含两部分工作流。
Page 1: Fill out developer app details and click on button to create Application ID, which opens, in a new tab...
第 1 页:填写开发者应用程序详细信息,然后单击按钮创建应用程序 ID,这会在新选项卡中打开...
Page 2: The App ID page. I need to copy the App ID from this page, then close the tab and go back to Page 1 and fill in the App ID (saved from Page 2), then submit the form.
第 2 页:App ID 页面。我需要从此页面复制 App ID,然后关闭选项卡并返回到第 1 页并填写 App ID(从第 2 页保存),然后提交表单。
I understand basic usage - how to open Page 1 and click the button which opens Page 2 - but how do I get a handle on Page 2 when it opens in a new tab?
我了解基本用法 - 如何打开第 1 页并单击打开第 2 页的按钮 - 但是如何在新选项卡中打开第 2 页时获得它的句柄?
Example:
例子:
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();
})();
Update 2017-10-25
更新 2017-10-25
- The work for Browser.pages has been completedand merged
- Fixes Emit new Page objects when new tabs created #386and Request: browser.currentPage() or similar way to access Pages #443.
- 对于Browser.pages工作已经完成,并合并
- 修复了在创建新选项卡时发出新的 Page 对象 #386和Request: browser.currentPage() 或访问 Pages #443 的类似方式。
Still looking for a good usage example.
仍在寻找一个好的用法示例。
回答by kaiak
A new patch has been committed two days ago and now you can use browser.pages()to access all Pages in current browser.
Works fine, tried myself yesterday :)
两天前提交了一个新补丁,现在您可以使用它browser.pages()来访问当前浏览器中的所有页面。工作正常,昨天自己试过:)
Edit:
编辑:
An example how to get a JSON value of a new page opened as 'target: _blank' link.
如何获取作为“目标:_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
回答by tchab
This will work for you in the latest alpha branch:
这将在最新的 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
Be sure to use the last puppeteer version (from Github master branch) by setting package.jsondependency to
通过将package.json依赖项设置为,确保使用最新的 puppeteer 版本(来自 Github master 分支)
"dependencies": {
"puppeteer": "git://github.com/GoogleChrome/puppeteer"
},
Source: JoelEinbinder @ https://github.com/GoogleChrome/puppeteer/issues/386#issuecomment-343059315
来源:JoelEinbinder @ https://github.com/GoogleChrome/puppeteer/issues/386#issuecomment-343059315
回答by Grant Miller
According to the Official Documentation:
根据官方文档:
browser.pages()
- returns:
<Promise<Array<Page>>>Promise which resolves to an array of all open pages. Non visible pages, such as"background_page", will not be listed here. You can find them usingtarget.page().An array of all pages inside the Browser. In case of multiple browser contexts, the method will return an array with all the pages in all browser contexts.
浏览器页面()
- 返回:Promise 解析为所有打开页面的数组。不可见的页面,例如,将不会在此处列出。您可以使用.
<Promise<Array<Page>>>"background_page"target.page()浏览器中所有页面的数组。在多个浏览器上下文的情况下,该方法将返回一个包含所有浏览器上下文中所有页面的数组。
Example Usage:
示例用法:
let pages = await browser.pages();
await pages[0].evaluate(() => { /* ... */ });
await pages[1].evaluate(() => { /* ... */ });
await pages[2].evaluate(() => { /* ... */ });
回答by Giovanni Bitliner
You could remove the need to switch page in case it is caused by target="_blank"attribute - by setting target="_self"
如果是由target="_blank"属性引起的,您可以消除切换页面的需要- 通过设置target="_self"
Example:
例子:
element = page.$(selector)
await page.evaluateHandle((el) => {
el.target = '_self';
}, element)
element.click()
回答by browserless
If your click action is emitting a pageload, then any subsequent scripts being ran are effectively lost. To get around this you need to trigger the action (a click in this case) but notawaitfor it. Instead, wait for the pageload:
如果您的点击操作正在发出页面加载,那么正在运行的任何后续脚本实际上都将丢失。要解决此问题,您需要触发操作(在本例中为单击),但不是await为它触发。相反,等待页面加载:
page.click('.get-appId');
await page.waitForNavigation();
This will allow your script to effectively wait for the next pageload event before proceeding with further actions.
这将允许您的脚本在继续执行进一步操作之前有效地等待下一个页面加载事件。
回答by krukid
In theory, you could override the window.openfunction to always open "new tabs" on your current page and navigate via history.
理论上,您可以覆盖该window.open功能以始终在当前页面上打开“新标签”并通过历史记录进行导航。
Your workflow would then be:
您的工作流程将是:
Override the
window.openfunction:await page.evaluateOnNewDocument(() => { window.open = (url) => { top.location = url } })Go to your first page and perform some actions:
await page.goto(PAGE1_URL) // ... do stuff on page 1Navigate to your second page by clicking the button and perform some actions there:
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(() => { ... })Return to your first page:
await page.goBack() // or: await page.goto(PAGE1_URL) // ... do stuff on page 1, injecting info saved from page 2
覆盖
window.open函数:await page.evaluateOnNewDocument(() => { window.open = (url) => { top.location = url } })转到您的第一页并执行一些操作:
await page.goto(PAGE1_URL) // ... do stuff on page 1单击按钮导航到您的第二页并在那里执行一些操作:
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(() => { ... })返回您的第一页:
await page.goBack() // or: await page.goto(PAGE1_URL) // ... do stuff on page 1, injecting info saved from page 2
This approach, obviously, has its drawbacks, but I find it simplifies multi-tab navigation drastically, which is especially useful if you're running parallel jobs on multiple tabs already. Unfortunately, current API doesn't make it an easy task.
显然,这种方法有其缺点,但我发现它极大地简化了多标签导航,如果您已经在多个标签上运行并行作业,这尤其有用。不幸的是,当前的 API 并没有使它成为一项简单的任务。
回答by Thomas Walpole
You can't currently - Follow https://github.com/GoogleChrome/puppeteer/issues/386to know when the ability is added to puppeteer (hopefully soon)
您目前不能 - 按照https://github.com/GoogleChrome/puppeteer/issues/386了解何时将功能添加到 puppeteer(希望很快)
回答by neaumusic
it looks like there's a simple 'page.popup' event
Page corresponding to "popup" window Emitted when the page opens a new tab or window.
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')), ]);
对应于“弹出”窗口的页面 当页面打开一个新选项卡或窗口时发出。
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')), ]);

