javascript - Puppeteer: Jednoduchší spôsob, ako pracovať so stránkami vytvorenými kliknutím na a[target="_blank"]; počkať na načítanie a zahrnúť časové limity
Prehľad
Hľadám jednoduchší spôsob, ako riešiť kliknutie na odkazy, ktoré otvárajú nové stránky (napríklad anchor tagy s target="_blank").
Tu znamená handle:
- získať nový objekt stránky
- počkaj, kým sa načíta nová karta (s časovým limitom)
Kroky na reprodukciu
Povedzte nám o vašom prostredí:
- Verzia Puppeteer: ^1.11.0
- Platforma/verzia OS: 64-bit, Windows 10 Pro
- URL adresy (ak je to relevantné): žiadne
- Verzia Node.js: v10.15.0
Pozrel som sa na súvisiace problémy:
https://github.com/GoogleChrome/puppeteer/issues/386 https://github.com/GoogleChrome/puppeteer/issues/3535 https://github.com/GoogleChrome/puppeteer/issues/978 a ďalšie
Aké kroky spôsobia problém?
Zahrnul som nižšie uvedený úryvok kódu
Snažím sa:
Získajte objekt pre novú stránku, keď kliknutie na odkaz otvorí novú kartu. (Odkazy sú generované dynamicky, zachytenie href nemusí byť najvhodnejší spôsob)
Počkajte, kým sa načíta nová stránka (s časovým limitom). Bol by som rád, keby ste na zachovanie konzistencie mohli použiť page.waitForNavigation
zatvorte kartu a vráťte sa na predchádzajúcu kartu, aby ste pokračovali v ďalších operáciách
Prosím, zahrňte kód, ktorý reprodukuje problém.
// 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;
Aký je očakávaný výsledok?
Jednoduchší a konzistentný spôsob, ako spravovať nové záložky
Čo sa deje namiesto toho?
Vývojár musí písať to, čo vyzerá ako rozvody (interné/nízkoúrovňové) príkazy.
Použitie waitForTarget by to mohlo zjednodušiť, ale nepodarilo sa mi, aby predikát vracal správne typy. Tu je môj nefunkčný kód
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)
Poznámka: issue, ktorú som vytvoril na github: https://github.com/GoogleChrome/puppeteer/issues/3718
Odpoveď č. 1
najprv spustite funkciu click predovšetkým a odstráňte "global" vo vnútri promise a deklarujte browser ako konštantu mimo promise
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;
