Puppeteer Page.setRequestInterception() 全解析:开启请求拦截、阻塞与改写网络请求的完整指南
【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer
网络拦截是浏览器自动化中高频使用的核心能力。本文以 Puppeteer 官方 API 文档 Page.setRequestInterception() 为主线,系统讲解该方法的方法签名、拦截开启后的请求停滞模型、配套的 HTTPRequest.abort() / HTTPRequest.continue() / HTTPRequest.respond() 三件套,并结合仓库中的源码与示例,给出可直接运行的实战代码。读完本文,你将掌握拦截任意请求并实现"屏蔽图片、改写请求头、模拟响应、阻断特定域名"等方案的完整技能。
方法签名与语义
setRequestInterception()在 Page 抽象类 中被声明为抽象方法,其完整签名如下:
class Page { abstract setRequestInterception(value: boolean): Promise<void>; }对应的接口定义位于仓库 packages/puppeteer-core/src/api/Page.ts。参数说明如下:
| 参数 | 类型 | 说明 |
|---|---|---|
value | boolean | 是否开启请求拦截。传true开启,传false关闭 |
调用会返回Promise<void>。从源码结构看,该方法在 Page 抽象类 中仅为abstract声明,实际行为由不同浏览器引擎的实现类提供——CDP(Chrome DevTools Protocol)方向的实现在 packages/puppeteer-core/src/cdp/Page.ts,WebDriver BiDi 方向的实现在 packages/puppeteer-core/src/bidi/Page.ts,二者在网络请求的底层拦截与"放行/中止/应答"协调逻辑最终汇聚到对应的网络管理器(例如 packages/puppeteer-core/src/cdp/NetworkManager.ts)。
开启拦截的三个直接效果
官方文档明确了拦截开启后的行为语义,理解这一点是正确使用的前提:
- 激活三件套能力:开启请求拦截后,HTTPRequest.abort()(中止请求)、HTTPRequest.continue()(继续请求)、HTTPRequest.respond()(伪造响应)三种方法才可用,从而获得修改页面发起的网络请求的能力。
- 请求停滞模型:一旦开启拦截,每一个请求都会停滞(stall),除非它被显式
continue(继续)、respond(应答)或abort(中止),或者命中浏览器缓存直接完成。这意味着一旦开启拦截却不在request事件中处理请求,页面导航与资源加载将被卡死。 - 幂等开启:由于方法接受布尔值,需要关闭拦截时调用
await page.setRequestInterception(false)即可。
提示:关闭拦截只影响之后的行为。如果你只在
request监听器里对满足条件的请求调用abort(),必须记得在else分支对不满足条件的请求调用continue(),否则页面会永久停滞。
一个容易忽略的联动点:HTTP 认证
值得留意的是,Page.authenticate()(HTTP 认证)的实现会在后台自动开启请求拦截(该方法文档中的:::note明确写道"Request interception will be turned on behind the scenes to implement authentication. This might affect performance.")。也就是说拦截与认证共享同一套底层机制,频繁开合拦截、叠加使用认证会带来性能开销,在使用时需要有所预期。
拦截的三把钥匙:abort / continue / respond
请求停滞之后,开发者正是通过监听request事件并调用 HTTPRequest 的三种终结方法来决定每个请求的去向。
abort:直接中止请求
签名与参数:
class HTTPRequest { abort(errorCode?: ErrorCode, priority?: number): Promise<void>; }| 参数 | 类型 | 说明 |
|---|---|---|
errorCode | ErrorCode(可选) | 提供给请求的错误码,缺省时由实现决定 |
priority | number(可选) | 若提供,则按"协作式处理"规则解析拦截;否则立即解析 |
其中 ErrorCode 是一组字符串字面量联合类型,完整取值如下:
export type ErrorCode = | 'aborted' | 'accessdenied' | 'addressunreachable' | 'blockedbyclient' | 'blockedbyresponse' | 'connectionaborted' | 'connectionclosed' | 'connectionfailed' | 'connectionrefused' | 'connectionreset' | 'internetdisconnected' | 'namenotresolved' | 'timedout' | 'failed';重要前提:abort 的官方说明 强调,使用它之前必须通过Page.setRequestInterception(true)开启拦截,否则该方法会立即抛出异常。这也是continue与respond两条文档中重复出现的前提约束。
continue:按原样或改写后继续请求
签名与参数:
class HTTPRequest { continue( overrides?: ContinueRequestOverrides, priority?: number, ): Promise<void>; }overrides的类型为 ContinueRequestOverrides,其可选字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
headers | Record<string, string> | 覆盖请求头 |
method | string | 覆盖 HTTP 方法 |
postData | string | 覆盖 POST 请求体 |
url | string | 改写请求 URL(注意:这是改写而非重定向,请求去向会改变) |
priority语义与abort一致:提供则按协作式规则解析,否则立即解析。一个改写请求头的标准示例:
await page.setRequestInterception(true); page.on('request', request => { // 覆盖请求头:设置 "foo",移除 "origin" const headers = Object.assign({}, request.headers(), { foo: 'bar', origin: undefined, }); request.continue({headers}); });respond:直接伪造响应
签名与参数:
class HTTPRequest { respond( response: Partial<ResponseForRequest>, priority?: number, ): Promise<void>; }response的类型为Partial<[ResponseForRequest](https://link.gitcode.com/i/724d897d3eb187f6590551e144f18075)>。其完整字段定义如下:
| 字段 | 类型 | 说明 |
|---|---|---|
status | number | HTTP 状态码 |
headers | Record<string, string \| string[] \| unknown>(可选) | 响应头。数组值会逐项映射为 String,用于同名多值头;非数组值转换为 String |
contentType | string | Content-Type 响应头 |
body | string \| Uint8Array | 响应体,支持字符串或二进制字节流 |
文档给出的"把所有请求都伪造成 404"的示例:
await page.setRequestInterception(true); page.on('request', request => { request.respond({ status: 404, contentType: 'text/plain', body: 'Not Found!', }); });注意:respond 的文档 特别说明,对
dataURL请求调用respond是一个 noop(空操作),即无法为data:协议请求伪造响应。
从文档示例出发:阻断全部图片请求
官方文档给出的"朴素版拦截器"直接演示了开启拦截 + 事件监听 + abort/continue 分流的最小完整闭环,代码位于 setRequestInterception 方法文档:
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', interceptedRequest => { if ( interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg') ) interceptedRequest.abort(); else interceptedRequest.continue(); }); await page.goto('https://example.com'); await browser.close();逐行拆解其执行脉络:
puppeteer.launch()启动浏览器,browser.newPage()新建标签页;await page.setRequestInterception(true)开启拦截——从此刻起页面发出的请求进入停滞状态;page.on('request', ...)注册监听器:每个请求到达时,先判断 URL 是否以.png/.jpg结尾;- 命中则调用
interceptedRequest.abort()让请求失败(页面表现为图片加载失败但页面主体继续渲染); - 未命中则调用
interceptedRequest.continue()放行,这是防止页面卡死的关键 else 分支; page.goto()导航并等待页面加载,最后browser.close()关闭。
更贴近实战的变体与扩展场景
仓库中的示例 examples/block-images.js 给出了一个更适合真实页面的写法:不再用 URL 后缀猜测,而是直接按资源类型判断。请求的资源类型可通过 HTTPRequest.resourceType() 获得(其类型定义为Lowercase<Protocol.Network.ResourceType>,即渲染引擎视角下的资源类型):
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', request => { if (request.resourceType() === 'image') { request.abort(); } else { request.continue(); } }); await page.goto('https://news.google.com/news/'); await page.screenshot({path: 'news.png', fullPage: true}); await browser.close();与官方文档示例相比,resourceType() === 'image'能一次性覆盖 CSS 背景图、<picture>源图、SVG 内嵌图等多种形态,而不仅限于.png/.jpg两种后缀,这也是该示例注释所强调的"屏蔽图片、节省带宽、加速整页截屏"用法。该模式正是做整页截图、页面性能分析时去除干扰资源的常用手段。
在真实工程中,你往往还需要把这些手法组合成更细的规则。下面给出一个可运行的综合示例:同时屏蔽图片、改写第三方统计请求头、并把某个 API 的请求伪造成本地数据:
import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', request => { const url = request.url(); // 1) 屏蔽图片类资源 if (request.resourceType() === 'image') { return request.abort(); } // 2) 伪造对特定接口的响应(例如注入桩数据) if (url.endsWith('/api/config')) { return request.respond({ status: 200, contentType: 'application/json', body: JSON.stringify({featureFlags: {newHome: true}}), }); } // 3) 对第三方统计域名只放行但附加标记头 if (url.includes('analytics.example.com')) { return request.continue({ headers: {...request.headers(), 'x-pptr-tagged': '1'}, }); } // 4) 其余请求一律放行 return request.continue(); }); await page.goto('https://example.com'); await browser.close();底层实现与注意事项(源码视角)
最后回到仓库源码层面,帮助你建立对这套机制的准确预期:
- 接口抽象:Page.setRequestInterception 在 API 层只是抽象声明,参数
value的 JSDoc 注释与公开文档逐字对应("Whether to enable request interception"),保证文档与类型签名一致。 - 引擎分派:Chrome/Chromium 走 CDP 实现(cdp/Page.ts 及 cdp/NetworkManager.ts),Firefox 等走 WebDriver BiDi 实现(bidi/Page.ts),底层分别依赖各自的 Fetch / Network 域协议。仓库测试 NetworkManager.test.ts 覆盖了拦截逻辑的编排细节。
- 性能与纪律:开启拦截后"每个请求都会停滞"意味着所有网络事件都要经过 Node 侧处理再放回浏览器,吞吐必然下降;因此拦截只应在确有需要的场景开启,处理完毕记得
setRequestInterception(false)关闭。另外务必保证每个请求都有且仅有一次终结调用,并尽量让abort/continue/respond的分支互斥,避免同一请求被重复解析。
小结
page.setRequestInterception(true)开启拦截后,页面请求全部停滞,必须通过request事件对每个请求调用abort()/continue()/respond()之一,或让请求命中浏览器缓存。abort(errorCode?)直接中止请求并支持 14 种 ErrorCode;continue(overrides?)可改写请求头、方法、POST 体甚至 URL;respond(response?)可用 ResponseForRequest 字段伪造状态码、响应头与响应体。- 未开启拦截就调用三件套会立即抛异常;
dataURL请求无法被respond伪造;HTTP 认证会在后台自动开启拦截。 - 结合 examples/block-images.js 的资源类型判断写法,可以低成本实现图片屏蔽、请求改写与响应桩等常见自动化需求。
【免费下载链接】puppeteerJavaScript API for Chrome and Firefox项目地址: https://gitcode.com/GitHub_Trending/puppeteer1/puppeteer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考