Кукольник: как работать с несколькими вкладками?

Сценарий: веб-форма для регистрации приложений для разработчиков с двумя рабочими процессами.

Страница 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, чтобы узнать, когда способность добавлена ​​к кукловоду (надеюсь, скоро)