网络爬取中XHR请求拦截与页面异步请求统计方法
做网络数据爬取时,要拦截XHR请求、准确统计页面加载窗口期触发的Ajax/Fetch类异步请求总数,可根据你的爬取技术栈、目标站反爬强度选以下三类方案,都能拿到准确结果:
1. 原生JS钩子注入拦截
这是最轻量的实现方式,核心逻辑是在页面脚本执行前,重写浏览器原生的XHR和Fetch方法,在请求发起的节点做计数,同时绑定页面load事件作为统计截止点,避免把加载完成后用户交互触发的请求算进去。
注意:现在大部分站点的异步请求同时存在传统XHR和Fetch两种实现,必须同时拦截两类方法,否则会漏统计。
参考实现代码:
// 初始化计数器,标记页面加载状态 const asyncReqStats = { xhr: 0, fetch: 0, total: 0 }; let isPageLoaded = false; // 重写XHR原型方法 const originXHROpen = XMLHttpRequest.prototype.open; const originXHRSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, reqUrl) { this._reqUrl = reqUrl; this._reqMethod = method; return originXHROpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function () { if (!isPageLoaded) { asyncReqStats.xhr += 1; asyncReqStats.total += 1; } return originXHRSend.apply(this, arguments); }; // 重写Fetch方法 const originFetch = window.fetch; window.fetch = function (input, config) { if (!isPageLoaded) { asyncReqStats.fetch += 1; asyncReqStats.total += 1; } return originFetch.apply(this, arguments); }; // 页面完全加载后停止计数 window.addEventListener('load', () => { isPageLoaded = true; console.log('页面加载阶段异步请求统计:', asyncReqStats); });
这个方案的缺点是如果目标站有反爬检测,会识别到原生方法被改写,容易触发拦截。
2. 浏览器调试协议层面拦截
如果用Puppeteer、Playwright、Selenium这类自动化爬取框架,直接基于Chrome DevTools Protocol(CDP)监听网络事件即可,不需要注入脚本改写原生方法,隐蔽性更强,统计也更准确,还能自动识别iframe里的子页面请求,不会漏算。
参考Playwright下的实现逻辑:
const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch(); const page = await browser.newPage(); let asyncReqTotal = 0; let stopCount = false; // 全局监听所有网络请求 page.on('request', (req) => { const type = req.resourceType(); // 仅统计加载阶段的xhr、fetch类请求 if (!stopCount && (type === 'xhr' || type === 'fetch')) { asyncReqTotal += 1; } }); // 等待页面完全加载 await page.goto('目标爬取页面地址', { waitUntil: 'load' }); stopCount = true; console.log(`页面加载阶段共触发${asyncReqTotal}个异步请求`); await browser.close(); })();
这个方案是目前自动化爬取场景下最常用的,稳定性足够,也能绕过大部分基础反爬检测。
3. 中间人代理层流量拦截
如果目标站反爬强度极高,会检测浏览器环境、甚至改写原生请求逻辑,就直接在传输层做统计:把爬取流量全部转发到Mitmproxy这类中间人代理,代理侧记录所有经过的请求,同时由爬取端给代理发送「页面开始加载」「页面加载完成」的时间标记,在标记的时间窗口内筛选出接口类异步请求(可通过请求头X-Requested-With: XMLHttpRequest标识、返回值格式为JSON、接口路径特征等维度筛选)即可完成统计。
这个方案完全不侵入页面运行环境,不管前端做什么混淆、改写,都能拿到全量流量数据,统计准确率最高,缺点是需要额外搭建代理环境,配置成本稍高。
统计注意事项
- 不要漏统计同域/跨域iframe内嵌页面里触发的异步请求
- 注意过滤静态资源请求(js/css/图片/字体等),不要把这类资源请求算进Ajax异步请求的统计里
- 要做去重处理,避免把前端自动重试、预加载触发的重复请求重复计数
- 一定要以页面
load事件作为统计截止锚点,不要把加载完成后鼠标滚动、点击等交互触发的请求算入页面加载阶段的统计范围
内容的提问来源于stack exchange,提问作者Amritesh

