Puppeteer 如何实现网络请求拦截?有哪些实际应用场景?
Puppeteer 通过 CDP(Chrome DevTools Protocol)提供的 Network 域能力实现请求拦截,核心 API 是 page.setRequestInterception(true)。启用后,每个请求都会被暂停,必须手动调用 continue()、abort() 或 respond() 才能放行。这一机制在爬虫加速、接口 Mock、安全测试等场景中非常实用。
启用请求拦截的基本方式
javascriptconst 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 —— 放行请求
直接放行原始请求,也可以在放行的同时修改请求参数:
javascriptpage.on("request", (request) => { // 修改请求头后放行 request.continue({ headers: { ...request.headers(), Authorization: "Bearer token123", }, }); });
continue() 支持覆盖 url、method、postData、headers 四个字段,可以实现请求重定向、修改 POST 数据等操作。
abort —— 终止请求
直接阻止请求发出,常用于屏蔽广告、图片、字体等非必要资源:
javascriptpage.on("request", (request) => { if (request.resourceType() === "image") { request.abort(); } else { request.continue(); } });
abort() 可传入错误码,默认是 failed,常用值包括 aborted、accessdenied、connectionrefused 等。
respond —— 直接返回响应
不向服务器发送请求,直接在本地构造响应返回。这是接口 Mock 的核心手段:
javascriptpage.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() 支持 status、headers、contentType、body 四个字段,可以完整模拟服务器行为。
响应监听 —— 获取服务端返回数据
通过 response 事件监听服务端实际返回的内容,常用于数据采集和接口监控:
javascriptpage.on("response", async (response) => { if (response.url().includes("/api/data")) { const data = await response.json(); console.log("接口返回:", data); } });
注意:response.json() 只能调用一次,且只有 JSON 格式的响应才能解析。
资源类型过滤
request.resourceType() 返回请求的资源类型,可用于批量过滤:
javascriptconst blockedTypes = ["image", "font", "stylesheet", "media"]; page.on("request", (request) => { if (blockedTypes.includes(request.resourceType())) { request.abort(); } else { request.continue(); } });
Puppeteer 支持的资源类型包括:document、stylesheet、image、media、font、script、xhr、fetch、websocket、eventsource、manifest、texttrack、other。
实际应用场景
爬虫加速:屏蔽非必要资源
爬取数据时,图片、字体、CSS 对数据提取无用,屏蔽后页面加载速度可提升 50% 以上:
javascriptawait 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 服务:
javascriptconst 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(); });
广告与追踪屏蔽
屏蔽已知广告域名和追踪脚本,减少无关请求:
javascriptconst 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 即可,无需在每个请求中手动处理:
javascriptpage.on("request", (request) => { request.continue({ headers: { ...request.headers(), Authorization: "Bearer your-token-here", }, }); });
网络请求监控与性能分析
记录所有请求和响应的时间戳与状态码,用于性能分析和接口排查:
javascriptconst 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);
错误处理
javascriptpage.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,核心能力一致。