Кукольник: как работать с несколькими вкладками?
Сценарий: веб-форма для регистрации приложений для разработчиков с двумя рабочими процессами.
Страница 1: Заполните подробные сведения о приложении разработчика и нажмите кнопку, чтобы создать идентификатор приложения, который открывается на новой вкладке...
Страница 2: Страница идентификатора приложения. Мне нужно скопировать идентификатор приложения с этой страницы, затем закройте вкладку и вернитесь на страницу 1 и заполните идентификатор приложения (сохраненный на странице 2), затем отправьте форму.
Я понимаю базовое использование - как открыть страницу 1 и нажать кнопку, которая открывается - но как мне получить дескриптор на странице 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
Ищем хороший пример использования.
Ответы
Ответ 1
Это будет работать для вас в последней альфа-ветке:
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
Обязательно используйте последнюю версию кукловода (из основной ветки Github), установив для package.json зависимость
"dependencies": {
"puppeteer": "git://github.com/GoogleChrome/puppeteer"
},
Источник: JoelEinbinder @https://github.com/GoogleChrome/puppeteer/issues/386#issuecomment-343059315
Ответ 2
Новый патч был совершен двумя днями назад, и теперь вы можете использовать browser.pages()
для доступа ко всем страницам в текущем браузере.
Хорошо работает, вчера пробовал:)
Edit:
Пример того, как получить значение JSON новой страницы, открытой как ссылка "target: _blank".
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
Ответ 3
Согласно официальной документации:
browser.pages()
- возвращает:
<
Promise
<
Array
<
Page
>>>
Promise, который разрешается в массив всех открытых страниц. Невидимые страницы, такие как "background_page"
, здесь не будут перечислены. Вы можете найти их, используя target.page()
.
Массив всех страниц внутри браузера. В случае нескольких контекстов браузера, метод вернет массив со всеми страницами во всех контекстах браузера.
Пример использования:
let pages = await browser.pages();
await pages[0].evaluate(() => { /* ... */ });
await pages[1].evaluate(() => { /* ... */ });
await pages[2].evaluate(() => { /* ... */ });
Ответ 4
В теории вы можете переопределить функцию window.open
, чтобы всегда открывать "новые вкладки" на текущей странице и перемещаться по истории.
Ваш рабочий процесс будет следующим:
-
Отмените функцию 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
Этот подход, очевидно, имеет свои недостатки, но я нахожу, что он значительно упрощает навигацию с несколькими вкладками, что особенно полезно, если вы уже выполняете параллельные задания на нескольких вкладках. К сожалению, текущий API не делает его легкой задачей.
Ответ 5
Вы можете удалить необходимость переключения страницы в случае, если она вызвана атрибутом target="_blank"
, установив target="_self"
Пример:
element = page.$(selector)
await page.evaluateHandle((el) => {
el.target = '_self';
}, element)
element.click()
Ответ 6
Если ваше действие щелчка генерирует загрузку страницы, то все последующие выполняемые сценарии будут эффективно потеряны. Чтобы обойти это, вам нужно запустить действие (щелчок в этом случае), но не await
его. Вместо этого дождитесь загрузки страницы:
page.click('.get-appId');
await page.waitForNavigation();
Это позволит вашему сценарию эффективно ждать следующего события загрузки страницы, прежде чем приступить к дальнейшим действиям.
Ответ 7
Вы не можете в настоящее время - Следуйте https://github.com/GoogleChrome/puppeteer/issues/386, чтобы узнать, когда способность добавлена к кукловоду (надеюсь, скоро)