5月28日 07:17

Puppeteer 如何实现网络请求拦截?有哪些实际应用场景?

Puppeteer 通过 CDP(Chrome DevTools Protocol)提供的 Network 域能力实现请求拦截,核心 API 是 page.setRequestInterception(true)。启用后,每个请求都会被暂停,必须手动调用 continue()abort()respond() 才能放行。这一机制在爬虫加速、接口 Mock、安全测试等场景中非常实用。

启用请求拦截的基本方式

javascript
const puppeteer = require("puppeteer"); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); // 启用请求拦截 await page.setRequestInterception(true); page.on("request", (request) => { // 每个请求必须被处理,否则页面会卡住 request.continue(); }); await page.goto("https://example.com"); await browser.close(); })();

关键点:setRequestInterception(true) 必须在页面导航前调用;每个被拦截的请求必须调用 continue()abort()respond() 之一,否则请求会一直挂起。

请求拦截的四种核心操作

continue —— 放行请求

直接放行原始请求,也可以在放行的同时修改请求参数:

javascript
page.on("request", (request) => { // 修改请求头后放行 request.continue({ headers: { ...request.headers(), Authorization: "Bearer token123", }, }); });

continue() 支持覆盖 urlmethodpostDataheaders 四个字段,可以实现请求重定向、修改 POST 数据等操作。

abort —— 终止请求

直接阻止请求发出,常用于屏蔽广告、图片、字体等非必要资源:

javascript
page.on("request", (request) => { if (request.resourceType() === "image") { request.abort(); } else { request.continue(); } });

abort() 可传入错误码,默认是 failed,常用值包括 abortedaccessdeniedconnectionrefused 等。

respond —— 直接返回响应

不向服务器发送请求,直接在本地构造响应返回。这是接口 Mock 的核心手段:

javascript
page.on("request", (request) => { if (request.url().includes("/api/user")) { request.respond({ status: 200, contentType: "application/json", body: JSON.stringify({ id: 1, name: "test-user" }), }); } else { request.continue(); } });

respond() 支持 statusheaderscontentTypebody 四个字段,可以完整模拟服务器行为。

响应监听 —— 获取服务端返回数据

通过 response 事件监听服务端实际返回的内容,常用于数据采集和接口监控:

javascript
page.on("response", async (response) => { if (response.url().includes("/api/data")) { const data = await response.json(); console.log("接口返回:", data); } });

注意:response.json() 只能调用一次,且只有 JSON 格式的响应才能解析。

资源类型过滤

request.resourceType() 返回请求的资源类型,可用于批量过滤:

javascript
const blockedTypes = ["image", "font", "stylesheet", "media"]; page.on("request", (request) => { if (blockedTypes.includes(request.resourceType())) { request.abort(); } else { request.continue(); } });

Puppeteer 支持的资源类型包括:documentstylesheetimagemediafontscriptxhrfetchwebsocketeventsourcemanifesttexttrackother

实际应用场景

爬虫加速:屏蔽非必要资源

爬取数据时,图片、字体、CSS 对数据提取无用,屏蔽后页面加载速度可提升 50% 以上:

javascript
await page.setRequestInterception(true); page.on("request", (request) => { const useless = ["image", "font", "stylesheet", "media"]; if (useless.includes(request.resourceType())) { request.abort(); } else { request.continue(); } });

接口 Mock:前后端联调

后端接口未就绪时,前端可以用 respond() 直接 Mock 数据,不依赖任何 Mock 服务:

javascript
const mockData = { "/api/users": { users: [{ id: 1, name: "Alice" }] }, "/api/posts": { posts: [{ id: 1, title: "Hello" }] }, }; page.on("request", (request) => { for (const [path, data] of Object.entries(mockData)) { if (request.url().includes(path)) { request.respond({ status: 200, contentType: "application/json", body: JSON.stringify(data), }); return; } } request.continue(); });

广告与追踪屏蔽

屏蔽已知广告域名和追踪脚本,减少无关请求:

javascript
const blockedDomains = ["ads.example.com", "analytics.example.com", "tracker.example.com"]; page.on("request", (request) => { if (blockedDomains.some((d) => request.url().includes(d))) { request.abort(); } else { request.continue(); } });

自动注入认证头

需要对所有请求添加 Token 时,用 continue() 覆盖 headers 即可,无需在每个请求中手动处理:

javascript
page.on("request", (request) => { request.continue({ headers: { ...request.headers(), Authorization: "Bearer your-token-here", }, }); });

网络请求监控与性能分析

记录所有请求和响应的时间戳与状态码,用于性能分析和接口排查:

javascript
const logs = []; page.on("request", (request) => { logs.push({ type: "request", url: request.url(), method: request.method(), resourceType: request.resourceType(), time: Date.now(), }); request.continue(); }); page.on("response", (response) => { logs.push({ type: "response", url: response.url(), status: response.status(), time: Date.now(), }); }); await page.goto("https://example.com"); console.log("请求总数:", logs.filter((l) => l.type === "request").length); console.log("响应总数:", logs.filter((l) => l.type === "response").length);

错误处理

javascript
page.on("requestfailed", (request) => { console.error("请求失败:", request.url()); console.error("原因:", request.failure()?.errorText); });

常见失败原因包括:网络断开、DNS 解析失败、SSL 证书错误、被 abort() 主动终止等。

面试追问与注意事项

Q:拦截对所有请求都会生效吗? 不是。导航请求(主文档请求)在部分场景下可能无法被拦截,且 WebSocket 升级请求的处理方式与普通 HTTP 请求不同。

Q:请求拦截对性能有什么影响? 启用拦截后,每个请求都要经过 JavaScript 事件循环处理,会增加请求延迟。对于高频请求场景(如 WebSocket 消息),建议按条件拦截而非全量拦截。

Q:如何避免重复处理请求? 调用 request.isInterceptResolutionHandled() 检查请求是否已被处理,避免在多个监听器中对同一请求重复调用 continue()abort()

Q:与 Playwright 的请求拦截有什么区别? Playwright 使用 page.route() API,支持路由模式匹配(如 page.route("**/api/**", handler)),语法更简洁。Puppeteer 则需要手动判断 URL。两者底层都基于 CDP,核心能力一致。

标签:Puppeteer