在Chrome中加载页面时,如何捕获所有网络请求和完整的响应数据?

时间:2018-10-24 12:45:28

标签: javascript google-chrome puppeteer

我要使用Puppeteer在Chrome中加载一个网址并捕获以下信息:

  • 请求网址
  • 请求标头
  • 请求发布数据
  • 响应标头文本(包括重复的标头,例如set-cookie
  • 传输的响应大小(即压缩后的大小)
  • 完整的回应正文

捕获整个响应主体是造成我问题的原因。

我尝试过的事情:

  • 使用response.buffer获取响应内容-自buffers are wiped on navigation起,如果在任何时候都存在重定向,则此操作将无效
  • 拦截请求并使用getResponseBodyForInterception-这意味着我可以no longer access the encodedLength,而且在某些情况下,我在获取正确的请求和响应标头方面也遇到了问题
  • 使用本地代理是可以的,但这会大大减慢页面加载时间(并更改了某些行为,例如证书错误)

理想情况下,该解决方案对性能的影响应该很小,并且与正常加载页面没有功能上的区别。我也想避免分叉Chrome。

6 个答案:

答案 0 :(得分:8)

您可以为每个请求使用page.setRequestInterception()启用请求拦截,然后在page.on('request')内部,可以使用request-promise-native模块充当中间人来收集响应数据在Puppeteer中继续向request.continue()发送请求之前。

这是一个完整的示例:

'use strict';

const puppeteer = require('puppeteer');
const request_client = require('request-promise-native');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  const result = [];

  await page.setRequestInterception(true);

  page.on('request', request => {
    request_client({
      uri: request.url(),
      resolveWithFullResponse: true,
    }).then(response => {
      const request_url = request.url();
      const request_headers = request.headers();
      const request_post_data = request.postData();
      const response_headers = response.headers;
      const response_size = response_headers['content-length'];
      const response_body = response.body;

      result.push({
        request_url,
        request_headers,
        request_post_data,
        response_headers,
        response_size,
        response_body,
      });

      console.log(result);
      request.continue();
    }).catch(error => {
      console.error(error);
      request.abort();
    });
  });

  await page.goto('https://example.com/', {
    waitUntil: 'networkidle0',
  });

  await browser.close();
})();

答案 1 :(得分:2)

我建议您搜索一个快速代理服务器,该服务器可以将请求日志与实际内容一起写入。

目标设置是允许代理服务器只写一个日志文件,然后分析日志,以搜索所需的信息。

在代理工作时不要拦截请求(这会导致速度变慢)

您可能遇到的性能问题(使用代理作为记录器设置)主要与TLS支持有关,请注意在代理设置中允许快速TLS握手,HTTP2协议

例如Squid benchmarks表明它能够处理数百个RPS,对于测试目的来说应该足够了

答案 2 :(得分:2)

仅限于伪造者的解决方案

这可以单独使用e来完成。您描述的response.buffer在导航中已清除的问题可以通过依次处理每个请求来避免。

工作原理

下面的代码使用page.setRequestInterception拦截所有请求。如果当前有一个正在处理/正在等待的请求,则将新请求放入队列。然后,可以使用response.buffer(),而不会出现其他请求可能会异步擦除缓冲区的问题,因为没有并行请求。一旦处理了当前处理的请求/响应,下一个请求将被处理。

代码

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch();
    const [page] = await browser.pages();

    const results = []; // collects all results

    let paused = false;
    let pausedRequests = [];

    const nextRequest = () => { // continue the next request or "unpause"
        if (pausedRequests.length === 0) {
            paused = false;
        } else {
            // continue first request in "queue"
            (pausedRequests.shift())(); // calls the request.continue function
        }
    };

    await page.setRequestInterception(true);
    page.on('request', request => {
        if (paused) {
            pausedRequests.push(() => request.continue());
        } else {
            paused = true; // pause, as we are processing a request now
            request.continue();
        }
    });

    page.on('requestfinished', async (request) => {
        const response = await request.response();

        const responseHeaders = response.headers();
        let responseBody;
        if (request.redirectChain().length === 0) {
            // body can only be access for non-redirect responses
            responseBody = await response.buffer();
        }

        const information = {
            url: request.url(),
            requestHeaders: request.headers(),
            requestPostData: request.postData(),
            responseHeaders: responseHeaders,
            responseSize: responseHeaders['content-length'],
            responseBody,
        };
        results.push(information);

        nextRequest(); // continue with next request
    });
    page.on('requestfailed', (request) => {
        // handle failed request
        nextRequest();
    });

    await page.goto('...', { waitUntil: 'networkidle0' });
    console.log(results);

    await browser.close();
})();

答案 3 :(得分:1)

按F12键进入Chrome浏览器,然后转到“网络”标签,您可以看到网站发送的所有http请求,就可以看到您提到的详细信息。

答案 4 :(得分:1)

我建议使用工具“ fiddler”。它将捕获您在加载URL URL时提到的所有信息。

答案 5 :(得分:-1)

这是我的解决方法,希望能帮助到其他人。

我遇到了 await page.setRequestInterception(True) 命令阻塞流的问题,并使页面挂起直到超时。

所以我加了这个功能

async def request_interception(req):
    """ await page.setRequestInterception(True) would block the flow, the interception is enabled individually """
    # enable interception
    req.__setattr__('_allowInterception', True)
    if req.url.startswith('http'):
        print(f"\nreq.url: {req.url}")
        print(f"  req.resourceType: {req.resourceType}")
        print(f"  req.method: {req.method}")
        print(f"  req.postData: {req.postData}")
        print(f"  req.headers: {req.headers}")
        print(f"  req.response: {req.response}")
    return await req.continue_()

移除了 await page.setRequestInterception(True) 并调用了上面的函数 page.on('request', lambda req: asyncio.ensure_future(request_interception(req))) 在我的 main() 中。

如果没有 req.__setattr__('_allowInterception', True) 语句,Pyppeteer 会抱怨没有为某些请求启用拦截,但它对我来说效果很好。

以防万一有人对我运行的 Pyppeteer 系统感兴趣: Ubuntu 20.04
Python 3.7 (venv)

...
pyee==8.1.0
pyppeteer==0.2.5
python-dateutil==2.8.1
requests==2.25.1
urllib3==1.26.3
websockets==8.1
...

我还在 https://github.com/pyppeteer/pyppeteer/issues/198

上发布了解决方案

干杯