javascript - Puppeteer: Mod mai simplu de a gestiona paginile create la clic pe a[target="_blank"]; așteaptă încărcarea și include timpi de expirare


Prezentare generală

Caut o modalitate mai simplă de a gestiona clicurile pe linkuri care deschid pagini noi (cum ar fi etichetele anchor cu target="_blank").

Aici, handle înseamnă:

  • obțineți noul obiect pagină
  • așteptați să se încarce noul tab (cu limită de timp)

Pași pentru a reproduce

Spune-ne despre mediul tău:

  • Versiunea Puppeteer: ^1.11.0
  • Versiune Platformă/OS: 64 de biți, Windows 10 Pro
  • URL-uri (dacă este cazul): niciunul
  • Versiunea Node.js: v10.15.0

Am analizat problemele conexe:

https://github.com/GoogleChrome/puppeteer/issues/386 https://github.com/GoogleChrome/puppeteer/issues/3535 https://github.com/GoogleChrome/puppeteer/issues/978 și altele

Ce pași vor reproduce problema?

Am inclus fragmentul de cod mai jos

Încerc să:

  1. Obțineți obiectul pentru pagina nouă atunci când faceți clic pe un link care deschide un tab nou. (Linkurile sunt generate dinamic, capturarea href-ului s-ar putea să nu fie cea mai elegantă metodă)

  2. Așteaptă până se încarcă noua pagină (cu timeout). Mi-ar plăcea dacă ai putea folosi page.waitForNavigation pentru consistență

  3. închide fila și revino la fila anterioară pentru a continua operațiunile

Vă rugăm să includeți codul care reproduce problema.

// as referenced here on #386 : https://github.com/GoogleChrome/puppeteer/issues/386#issuecomment-425109457
    const getNewPageWhenLoaded =  async () => {
        return new Promise(x =>
            global.browser.on('targetcreated', async target => {
                if (target.type() === 'page') {
                    const newPage = await target.page();
                    const newPagePromise = new Promise(y =>
                        newPage.once('domcontentloaded', () => y(newPage))
                    );
                    const isPageLoaded = await newPage.evaluate(
                        () => document.readyState
                    );
                    return isPageLoaded.match('complete|interactive')
                        ? x(newPage)
                        : x(newPagePromise);
                }
            })
        );
    };


const newPagePromise = getNewPageWhenLoaded();
await page.click('my-link'); // or just do await page.evaluate(() => window.open('https://www.example.com/'));
const newPage = await newPagePromise;

Care este rezultatul așteptat?

O modalitate mai ușoară și consistentă de a gestiona noile file

Ce se întâmplă în schimb?

Dezvoltatorul trebuie să scrie ceea ce pare a fi comenzi de tip instalații (interne/de nivel scăzut).

Utilizarea lui waitForTarget ar putea simplifica acest lucru, dar nu am reușit să fac ca predicatul să returneze tipurile corecte. Iată codul meu nefuncțional

private async getNewPageWhenLoaded() {
        const newTarget = await this._browser.waitForTarget(async (target) => {
            const newPage = await target.page();
            await newPage.waitForNavigation(this._optionsNavigation);
            // const newPagePromise = new Promise(() => newPage.once('load', () => x(newPage)));
            return await newPage.evaluate("true");
        });
        return await newTarget.page();
    }

// elsewhere in the code
            const newPagePromise = this.getNewPageWhenLoaded();
            await resultItem.element.click();
            const newPage = <Page>await newPagePromise;

//I get the following error
DevTools listening on ws://127.0.0.1:31984/devtools/browser/bf86648d-d52d-42d8-a392-629bf96211d4
(node:5564) UnhandledPromiseRejectionWarning: Error: Navigation failed because browser has disconnected!
    at CDPSession.LifecycleWatcher._eventListeners.helper.addEventListener (<path-to-my-project>\node_modules\puppeteer\lib\FrameManager.js:1181:107)
    at CDPSession.emit (events.js:182:13)
    at CDPSession._onClosed (<path-to-my-project>\node_modules\puppeteer\lib\Connection.js:231:10)
    at Connection._onMessage (<path-to-my-project>\node_modules\puppeteer\lib\Connection.js:103:19)
    at WebSocketTransport._ws.addEventListener.event (<path-to-my-project>\node_modules\puppeteer\lib\WebSocketTransport.js:41:24)
    at WebSocket.onMessage (<path-to-my-project>\node_modules\ws\lib\event-target.js:120:16)
    at WebSocket.emit (events.js:182:13)
    at Receiver.receiverOnMessage (<path-to-my-project>\node_modules\ws\lib\websocket.js:741:20)
    at Receiver.emit (events.js:182:13)
    at Receiver.dataMessage (<path-to-my-project>\node_modules\ws\lib\receiver.js:417:14)
  -- ASYNC --
    at Frame.<anonymous> (<path-to-my-project>\node_modules\puppeteer\lib\helper.js:144:27)
    at Page.waitForNavigation (<path-to-my-project>\node_modules\puppeteer\lib\Page.js:644:49)
    at Page.<anonymous> (<path-to-my-project>\node_modules\puppeteer\lib\helper.js:145:23)
    at newTarget._browser.waitForTarget (<path-to-my-project>\pageObjects\MyPage.js:104:27)
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:5564) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:5564) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:5564) UnhandledPromiseRejectionWarning: TimeoutError: Navigation Timeout Exceeded: 300000ms exceeded
    at Promise.then (<path-to-my-project>\node_modules\puppeteer\lib\FrameManager.js:1276:21)
  -- ASYNC --
    at Frame.<anonymous> (<path-to-my-project>\node_modules\puppeteer\lib\helper.js:144:27)
    at Page.waitForNavigation (<path-to-my-project>\node_modules\puppeteer\lib\Page.js:644:49)
    at Page.<anonymous> (<path-to-my-project>\node_modules\puppeteer\lib\helper.js:145:23)
    at newTarget._browser.waitForTarget (<path-to-my-project>\pageObjects\MyPage.js:104:27)
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:5564) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

Notă: problemă pe care am creat-o pe GitHub: https://github.com/GoogleChrome/puppeteer/issues/3718

Răspuns #1

mai întâi rulează funcția click mai întâi și elimină „global” din interiorul promisiunii și declară browserul ca constantă în afara promisiunii

const browser = await puppeteer.launch();
await page.click('my-link'); 
const getNewPageWhenLoaded =  async () => {
    return new Promise(x =>
        browser.on('targetcreated', async target => {
            if (target.type() === 'page') {
                const newPage = await target.page();
                const newPagePromise = new Promise(y =>
                    newPage.once('domcontentloaded', () => y(newPage))
                );
                const isPageLoaded = await newPage.evaluate(
                    () => document.readyState
                );
                return isPageLoaded.match('complete|interactive')
                    ? x(newPage)
                    : x(newPagePromise);
            }
        })
    );
};


const newPagePromise = getNewPageWhenLoaded();
const newPage = await newPagePromise;