木偶:返回AJAX响应的JSON响应

时间:2020-06-27 17:13:16

标签: node.js puppeteer

页面加载时,我试图等待页面发出的某些AJAX请求,然后返回其响应的JSON正文。即使在侦听器中满足“响应”事件的条件之后,我的代码也不会停止遍历每个响应。

找到要返回的响应后,如何捕获响应中的JSON,停止执行进一步加载页面的操作,然后返回JSON?

async function runScrape() {
    const browser = await browserPromise;
    const context = await browser.createIncognitoBrowserContext();
    const page = await context.newPage();
    await page.setDefaultTimeout(60000);
    
    let apiResponse;
    page.on('response', async response => {
        let url = await response.url();
        let status = await response.status();
        console.info(status + " NETWORK CALL: " + url);
        if ( url.match(requestPattern) ) {
            apiResponse = await response.text();
            await page.evaluate(() => window.stop());
        }
    });
    
    await page.goto(req.query.url);
    console.log("API RESPONSE:\n" + apiResponse);
    return apiResponse
}}

===更新===

这是最终起作用的解决方案。由于要抓取页面的特定行为,因此似乎需要此方法。

async function runScrape() {
    const browser = await browserPromise;
    const context = await browser.createIncognitoBrowserContext();
    const page = await context.newPage();
    await page.setDefaultTimeout(60000);
    await page.setRequestInterception(true);
    
    let JSONResponse;
    page.on('response', async response => {
        if ( !JSONResponse && response.url().match(requestPattern) ) {
            JSONResponse = await response.text();
        }
    });

    page.on('request', request => {
        if (request.resourceType() === 'image' || request.resourceType() === 'stylesheet') request.abort()
        else request.continue()
    });
    
    await page.goto(scrapeURL, {waitUntil: 'networkidle2'});
    await page.close();
    return JSONResponse
}

runScrape()
.then( response => {
    res.setHeader("content-type", "application/json");
    res.status(200).send(response);
})
.catch(err => {
    let payload = {"errorType": err.name, "errorMessage": err.message+"\n"+err.stack};
    console.error(JSON.stringify(payload));
    res.status(500).json(payload);
});

1 个答案:

答案 0 :(得分:0)

我将其简化为一个page.on('response'...,在这里我们用String.includes()寻找所需的请求模式。

确定响应后,我们可以使用await page.evaluate(() => window.stop())模拟浏览器的“停止加载此页面” 按钮。 window.stop()方法不会关闭浏览器,只是停止网络请求。

let resp
page.on('response', async response => {
  if (response.url().includes(requestPattern)) {
    resp = await response.json()
    await page.evaluate(() => window.stop())
    }
  })

await page.goto(req.query.url, { waitUntil: 'networkidle0' } )
console.log(resp)

编辑:

为避免undefined响应,应在waitUntil: 'networkidle0'上使用page.goto()设置,有关选项请参见the docs。您之所以拥有undefined,是因为默认情况下,在页面上触发load事件时,操纵up的人认为该页面已加载(这是waitUntil的默认设置)。因此,如果认为页面已加载,但是队列中仍然有网络连接,并且尚未找到您的请求模式:该脚本将从goto继续到console.log。因此,您可以等待所有网络请求完成,以确保请求已被注册。

networkidle0:当至少有500毫秒的网络连接不超过0个时,请考虑完成导航。

请注意::通过设置networkidle,您将无法在满足请求模式条件后断开连接,因此您无法停止响应的计划。

我建议中止那些不需要的resourceType,这样可能会产生与停止请求类似的结果:

例如:

将其放在page.on('response', async response => {...块结束之后。

  await page.setRequestInterception(true)
  page.on('request', request => {
    if (request.resourceType() === 'image' || request.resourceType() === 'stylesheet') request.abort()
    else request.continue()
  })

如果知道不需要哪些连接,也可以在request.url().includes(unwantedRequestPattern)条件下使用它。

相关问题