Estoy tratando de obtener una respuesta XHR de una página web. Encontré el
await page.waitForResponse(url);o
await page.waitForResponse((res) => { if (res.url() === myUrl) return true; });método, pero siempre se agota el tiempo de espera para la respuesta de URL que estoy tratando de obtener.
Sin embargo, si configuro
page.on('response', (res) => { if (res.url() === myUrl) { // do what I want with the response } })se encuentra la respuesta correcta y puedo recuperar los datos.
Después de algunas depuraciones, parece que waitForResponse() no devuelve ningún requerimiento/resolución XHR.
¿Alguna idea?
EDITAR: Ejemplo. Para este caso, se requiere usar el paquete puppeteer-extra-plugin-stealth y puppeteer-extra , de lo contrario, esta URL devolverá el código de estado '403':
import StealthPlugin from 'puppeteer-extra-plugin-stealth'; import UserAgent from 'user-agents'; import puppeteer from 'puppeteer-extra'; import { Page } from 'puppeteer'; const wantedUrl = 'https://www.nike.com.br/DataLayer/dataLayer'; const workingFunction = async (page: Page) => { let reqCount = 0; let resCount = 0; page.on('request', req => { reqCount++; if (req.url() == wantedUrl) { console.log('The request I need: ', req.url()); console.log(reqCount); } }); page.on('response', async res => { resCount++; if (res.url() == wantedUrl) { console.log('The response I need:', await res.json()); console.log(resCount); } }); await page.goto('https://www.nike.com.br/tenis-nike-sb-dunk-low-pro-unissex-153-169-229-284741', { timeout: 0, }); }; const notWorkingFunction = async (page: Page) => { let resCount = 0; await page.goto('https://www.nike.com.br/tenis-nike-sb-dunk-low-pro-unissex-153-169-229-284741'); const res = await page.waitForResponse( res => { resCount++; console.log(res.url()); console.log(resCount); if (res.url() === wantedUrl) { return true; } return false; }, { timeout: 0 } ); return res; }; (async () => { puppeteer.use(StealthPlugin()); const browser = await puppeteer.launch({}); const page = await browser.newPage(); const userAgent = new UserAgent({ deviceCategory: 'desktop' }); await page.setUserAgent(userAgent.random().toString()); try { // workingFunction(page); const res = await notWorkingFunction(page); } catch (e) { console.log(e); } })();La razón por la que funciona la versión page.on es porque establece los controladores de solicitud/respuesta antes de realizar la navegación . Por otro lado, la versión waitForResponse espera hasta que se activa el evento de "load" (punto de resolución predeterminado de page.goto() ), y solo entonces comienza a rastrear las respuestas con la llamada a page.waitForResponse . MDN dice del evento de load :
El evento de
loadse activa cuando se ha cargado toda la página, incluidos todos los recursos dependientes, como hojas de estilo e imágenes. Esto contrasta conDOMContentLoaded, que se activa tan pronto como se carga el DOM de la página, sin esperar a que los recursos terminen de cargarse.
En base a esto, podemos inferir que para cuando se activa el evento de load y la función waitForResponse finalmente comienza a escuchar el tráfico, ya se perdió la respuesta deseada, ¡así que espera para siempre!
La solución es crear la promesa para page.waitForResponse antes (o al mismo tiempo) de la llamada goto de modo que no se pierda tráfico cuando inicie la navegación.
También sugiero usar "domcontentloaded" en la llamada goto . "domcontentloaded" está infrautilizado en Puppeteer: no tiene sentido esperar a que lleguen todos los recursos cuando solo está buscando uno. La configuración predeterminada de "load" o "networkidleN" de uso frecuente es mejor para casos de uso como captura de pantalla de la página donde desea que todo se vea como lo vería un usuario. Para ser claros, esta no es la solución al problema, solo una optimización, y no es demasiado evidente en los documentos cuál es adecuado cuando.
Aquí hay un ejemplo mínimo (utilicé JS, no TS):
const puppeteer = require("puppeteer-extra"); // ^3.2.3 const StealthPlugin = require("puppeteer-extra-plugin-stealth"); // ^2.9.0 const UserAgent = require("user-agents"); // ^1.0.958 puppeteer.use(StealthPlugin()); let browser; (async () => { browser = await puppeteer.launch(); const [page] = await browser.pages(); const userAgent = new UserAgent({deviceCategory: "desktop"}); await page.setUserAgent(userAgent.random().toString()); const url = "https://www.nike.com.br/tenis-nike-sb-dunk-low-pro-unissex-153-169-229-284741"; const wantedUrl = "https://www.nike.com.br/DataLayer/dataLayer"; const [res] = await Promise.all([ page.waitForResponse(res => res.url() === wantedUrl, {timeout: 90000}), page.goto(url, {waitUntil: "domcontentloaded"}), ]); console.log(await res.json()); })() .catch(err => console.error(err)) .finally(() => browser?.close()) ;