- 网页爬虫
- 浏览器控制
- 测试
【免费下载链接】puppeteer-extra
💯 Teach puppeteer new tricks through plugins.
本文围绕 puppeteer-extra 生态中的
puppeteer-extra-plugin-block-resources插件展开:它借助 puppeteer 的请求拦截能力,按资源类型(图片、样式表、脚本、媒体等)精准屏蔽页面加载的次要资源,支持在运行时动态增删拦截类型,适用于爬虫提速、省流量、去广告图片、加速首屏渲染等场景。读完本文,你将掌握该插件的安装方式、两种配置用法(静态配置 + 运行时动态切换)、13 种可拦截资源类型的完整清单,以及其底层拦截实现原理与协作式拦截优先级的使用方法。
插件定位:为 puppeteer 加上"按类型断流"能力
puppeteer-extra-plugin-block-resources是 puppeteer-extra 插件生态中的一员(位于仓库 packages/puppeteer-extra-plugin-block-resources 目录),包名puppeteer-extra-plugin-block-resources。它的核心能力在包描述中写得非常直白:
Block resources (images, media, etc.) in puppeteer.
即"在 puppeteer 中屏蔽图片、媒体等资源"。它的完整特性由源码 JSDoc(index.js)给出:
- 支持所有资源类型:从
document到websocket共 13 种类型均可被拦截; - 拦截可动态切换:不必在启动前写死配置,运行中随时通过
add/delete增删要屏蔽的类型。
典型应用场景包括:
- 抓取页面时屏蔽图片、字体、媒体等非必要资源,显著降低带宽与耗时;
- 只关心 DOM 结构与脚本执行,屏蔽 CSS 以加速渲染;
- 屏蔽 favicon(
other类型)、弹窗广告图片等干扰资源; - 与
puppeteer-extra生态其他插件(如 stealth、adblocker)组合使用,构建更完整的自动化浏览器方案。
安装与依赖
该插件通过 npm/yarn 安装(详见 readme 与 package.json):
yarn add puppeteer-extra-plugin-block-resources # 或 npm i puppeteer-extra-plugin-block-resources从 package.json 可以看出它的依赖关系:
- 运行时依赖:
puppeteer-extra-plugin(^3.2.3,提供PuppeteerExtraPlugin基类)和debug(^4.1.1,提供调试日志); - peer 依赖:
puppeteer-extra(可选,作为插件宿主框架); - 开发依赖:
puppeteer(^2.0.0)、ava(2.4.0,测试框架)、npm-run-all。
注意:要真正使用它,你还需要在项目中安装puppeteer(或puppeteer-core)与puppeteer-extra本身。另外,从实现细节看(下文会展开),插件拦截逻辑依赖 puppeteer 的**协作式拦截(Cooperative Intercept Mode)**相关 API,因此建议在较新版本的 puppeteer 上使用,以完整发挥拦截能力。
快速上手:一次配置,全局生效
最简单的方式是在注册插件时通过opts一次性指定要屏蔽的类型(readme 中的官方示例):
const { DEFAULT_INTERCEPT_RESOLUTION_PRIORITY } = require('puppeteer') const puppeteer = require('puppeteer-extra') puppeteer.use(require('puppeteer-extra-plugin-block-resources')({ blockedTypes: new Set(['image', 'stylesheet']), // 可选:启用 Cooperative Mode,支持多个请求拦截器协同工作 interceptResolutionPriority: DEFAULT_INTERCEPT_RESOLUTION_PRIORITY }))这段代码做了三件事:
require('puppeteer-extra-plugin-block-resources')拿到的是一个工厂函数,调用它(传入配置对象)返回插件实例;puppeteer.use(...)把插件注册进 puppeteer-extra,之后每次puppeteer.launch()创建的浏览器、每个新页面都会自动带上拦截逻辑;blockedTypes: new Set(['image', 'stylesheet'])声明屏蔽图片与样式表两种资源。
注册完成后,页面中的所有图片和 CSS 请求都会被直接中止(abort),页面 DOM 与脚本仍正常加载。
API 详解:一个构造函数、两个 Set、一个优先级
该插件的公开 API 非常精简,完整定义见 readme 与 TypeScript 声明文件 index.d.ts。
Plugin 构造函数
Plugin继承自PuppeteerExtraPlugin基类,类型为function (opts),接受一个可选配置对象(默认{}):
| 配置项 | 类型 | 默认值 | 含义 |
|---|---|---|---|
opts.blockedTypes | Set<string> | 空Set(默认不拦截任何类型) | 指定要屏蔽的 resourceTypes |
opts.interceptResolutionPriority | number | undefined | 协作式拦截的解析优先级(可选) |
对应源码中defaultsgetter(index.js):
get defaults() { return { availableTypes: new Set([ 'document', 'stylesheet', 'image', 'media', 'font', 'script', 'texttrack', 'xhr', 'fetch', 'eventsource', 'websocket', 'manifest', 'other' ]), // Block nothing by default blockedTypes: new Set([]), interceptResolutionPriority: undefined } }值得一提的实现细节:基类
PuppeteerExtraPlugin的构造函数会用merge-deep把插件defaults与用户传入的opts深度合并,合并结果通过this.opts访问(见 packages/puppeteer-extra-plugin/src/index.ts)。因此blockedTypes如果没传,就会被defaults中的空Set兜底,保证插件"默认什么都不拦、随时可以开拦"的语义。
availableTypes:获取全部可拦截的资源类型
get availableTypes() { return this.defaults.availableTypes }availableTypes是一个只读 getter(index.js),返回全部 13 种可用资源类型,类型为Set<string>:
document,stylesheet,image,media,font,script,texttrack,xhr,fetch,eventsource,websocket,manifest,other
blockedTypes:读取/修改当前屏蔽集合
get blockedTypes() { return this.opts.blockedTypes }blockedTypes(index.js)返回当前被屏蔽的类型集合。正如 readme 所说,它既可以通过opts初始化,也可以在运行时直接修改这个属性(因为 getter 直接引用了this.opts.blockedTypes这个 Set 对象,外部add/delete会同步作用于插件内部逻辑)——这正是"动态切换"能力的来源。
interceptResolutionPriority:协作式拦截优先级
get interceptResolutionPriority() { return this.opts.interceptResolutionPriority }该 getter(index.js)暴露协作式拦截的优先级数值,同样支持"配置时传入"或"运行时修改"两种方式。关于它的作用,见下文"协作式拦截模式"一节。
13 种资源类型全清单
availableTypes中的 13 种类型对应 Chromium 的资源类型分类,含义如下(这是 Chrome DevTools Protocol 中Network.resourceType的标准分类,插件只是把它们原样暴露):
| 资源类型 | 典型含义 |
|---|---|
document | 主文档 / 顶层导航请求(HTML 页面本身) |
stylesheet | CSS 样式表 |
image | 图片资源 |
media | 音视频媒体(如<video>/<audio>源) |
font | 字体文件(如 woff2、ttf) |
script | JavaScript 脚本 |
texttrack | 文本轨道(如视频字幕 VTT) |
xhr | XMLHttpRequest 异步请求 |
fetch | Fetch API 请求 |
eventsource | Server-Sent Events(EventSource 长连接) |
websocket | WebSocket 连接 |
manifest | Web App Manifest / 应用清单 |
other | 其他类型,例如 favicon |
拦截document意味着整页导航都会被中止,实际使用中很少这么做;最常见的组合是image、stylesheet、media、font这类"非关键"资源。示例代码注释里就专门标注了other的典型用例是 favicon(example.js)。
动态切换:运行时随心所欲地开/关拦截
该插件最突出的设计是运行时动态调整。readme 给出了完整示例(readme),仓库中的 example.js 与之几乎一致、可直接运行:
const { DEFAULT_INTERCEPT_RESOLUTION_PRIORITY } = require('puppeteer') const puppeteer = require('puppeteer-extra') const blockResourcesPlugin = require('puppeteer-extra-plugin-block-resources')({ // 可选:启用 Cooperative Mode,支持多个请求拦截器协同工作 interceptResolutionPriority: DEFAULT_INTERCEPT_RESOLUTION_PRIORITY }) puppeteer.use(blockResourcesPlugin) ;(async () => { const browser = await puppeteer.launch({ headless: false }) const page = await browser.newPage() // 第一站:只屏蔽图片 blockResourcesPlugin.blockedTypes.add('image') await page.goto('http://www.msn.com/', { waitUntil: 'domcontentloaded' }) // 第二站:追加屏蔽样式表与 other(如 favicon) blockResourcesPlugin.blockedTypes.add('stylesheet') blockResourcesPlugin.blockedTypes.add('other') await page.goto('http://news.ycombinator.com', { waitUntil: 'domcontentloaded' }) // 第三站:移除样式表与 other,改屏蔽媒体与脚本 blockResourcesPlugin.blockedTypes.delete('stylesheet') blockResourcesPlugin.blockedTypes.delete('other') blockResourcesPlugin.blockedTypes.add('media') blockResourcesPlugin.blockedTypes.add('script') await page.goto('http://www.youtube.com', { waitUntil: 'domcontentloaded' }) console.log('all done') })()这段代码演示了三个关键点:
- 保留插件实例引用:必须把
blockResourcesPlugin实例存下来,才能在运行时操作它的blockedTypes; - Set 的
add/delete即改即生效:因为拦截判断每次请求时都会实时读取blockedTypes(见下文onRequest实现),所以无需重建浏览器或页面; - 按页面切换屏蔽策略:同一浏览器进程内,访问不同站点时采用不同的拦截组合,比如访问 YouTube 时屏蔽脚本和媒体以大幅减少流量。
源码级原理:从注册到拦截的完整调用链
了解了用法,再看它是如何工作的。整个拦截流程分三步:插件注册 → 页面创建时开启拦截 → 每个请求到来时按类型裁决。
第一步:注册插件,钩入页面生命周期
puppeteer.use(plugin)会把插件注册进 puppeteer-extra 的插件列表,并调用plugin._register()记录其子类成员(packages/puppeteer-extra/src/index.ts)。
随后在launch()/connect()时,基类会为每个新建页面触发onPageCreated生命周期钩子:浏览器targetcreated事件 → 基类的_onTargetCreated过滤出type === 'page'的目标 → 调用插件的onPageCreated(packages/puppeteer-extra-plugin/src/index.ts)。block-resources 插件正是利用这个钩子完成拦截装配。
第二步:onPageCreated,开启请求拦截
async onPageCreated(page) { this.debug('onPageCreated', { blockedTypes: this.blockedTypes }) await page.setRequestInterception(true) page.on('request', this.onRequest.bind(this)) }这段代码(index.js)做了两件事:
page.setRequestInterception(true):开启 puppeteer 的请求拦截开关,让页面的每个网络请求都以request事件的形式交给 Node.js 侧裁决;page.on('request', this.onRequest.bind(this)):把裁决函数绑定到该页面的请求事件上。
因为是"每个新页面"都会走一遍onPageCreated,所以launch()之后browser.newPage()创建的任意页面(包括弹窗页面)都会被自动覆盖。
第三步:onRequest,按资源类型裁决放行或中止
onRequest(request) { const type = request.resourceType() const shouldBlock = this.blockedTypes.has(type) // Requests are immediately handled if not using Cooperative Intercept Mode const alreadyHandled = request.isInterceptResolutionHandled ? request.isInterceptResolutionHandled() : true this.debug('onRequest', { type, shouldBlock, alreadyHandled }) if (alreadyHandled) return if (shouldBlock) { const abortArgs = request.abortErrorReason ? ['blockedbyclient', this.interceptResolutionPriority] : [] return request.abort(...abortArgs) } const continueArgs = request.continueRequestOverrides ? [request.continueRequestOverrides(), this.interceptResolutionPriority] : [] return request.continue(...continueArgs) }这段核心逻辑(index.js)可以拆成四步理解:
- 取类型:
request.resourceType()拿到该请求的资源类型(即上面 13 种之一); - 查集合:
this.blockedTypes.has(type)判断它是否在当前屏蔽集合中——这里每次都实时读取,所以动态add/delete立即生效; - 防重复处理:
isInterceptResolutionHandled()是协作式拦截模式提供的查询 API,用于确认该请求是否已被其他拦截器处理过;若已被处理则直接返回,避免多个拦截器互相覆盖。从注释 "Requests are immediately handled if not using Cooperative Intercept Mode" 可以看出,在不具备该 API 的环境下插件选择保守跳过,防止多拦截器冲突; - 裁决动作:
- 命中屏蔽类型 →
request.abort('blockedbyclient')中止请求(若支持协作模式则带上错误原因与优先级); - 未命中 →
request.continue()放行(若支持协作模式则合并覆盖项与优先级)。
- 命中屏蔽类型 →
补充说明:
request.resourceType()返回的document等类型与availableTypes一一对应,这正是拦截判断的匹配依据。值得注意,页面本身的主文档请求也会进入onRequest,因此理论上也可以屏蔽document——但那样页面将无法加载,实际场景中请谨慎使用。
协作式拦截模式(Cooperative Intercept Mode):多个拦截器如何共存
当你的 puppeteer-extra 同时注册了多个会拦截请求的插件(例如 block-resources 与 adblocker 一起使用),就会面临"多个request监听器争抢同一个请求"的问题。puppeteer 的Cooperative Intercept Mode(协作式拦截)就是为此设计的机制:
- 每个拦截器在调用
request.abort()/request.continue()时附带一个优先级数值(priority); - puppeteer 内部按优先级裁决由谁处理:相同优先级下先到先得,已处理(
isInterceptResolutionHandled()为true)的请求后续拦截器不再干预; DEFAULT_INTERCEPT_RESOLUTION_PRIORITY是 puppeteer 导出的默认优先级常量,从require('puppeteer')中解构获取。
插件对协作模式的支持体现在三处 API 的使用(index.js):
request.isInterceptResolutionHandled():查询请求是否已被更高/同优先级拦截器处理;request.abortErrorReason/request.continueRequestOverrides:读取已记录的裁决信息,避免覆盖;this.interceptResolutionPriority:作为 abort/continue 的第三参数传入。
因此,当你在同一个 puppeteer-extra 实例里组合多个拦截型插件时,建议像官方示例那样显式传入interceptResolutionPriority: DEFAULT_INTERCEPT_RESOLUTION_PRIORITY,让各拦截器在同一优先级下公平协作;而如果你希望 block-resources 的裁决优先级更高(先于其他拦截器生效),可以传入一个更大的数值——具体数值含义与 puppeteer 版本相关,使用前请查阅你所安装 puppeteer 版本对协作式拦截的说明。
测试与调试:如何验证插件行为
单元测试
仓库为插件编写了基于 ava 的单元测试(index.test.js),覆盖了四类断言:
- 导出形态:
require('.')返回一个函数(工厂); - 类结构完整性:实例名称是
block-resources、具备_isPuppeteerExtraPlugin标记,原型上应恰好包含 8 个成员:constructor、name、defaults、availableTypes、blockedTypes、interceptResolutionPriority、onRequest、onPageCreated; - 默认值:
opts.blockedTypes为空Set、availableTypes恰好 13 项、interceptResolutionPriority为undefined。
# 在该包目录下运行测试 cd packages/puppeteer-extra-plugin-block-resources yarn test调试日志
插件基于debug库输出调试信息,命名空间为puppeteer-extra-plugin:block-resources(基类this.debug的自动命名规则见 packages/puppeteer-extra-plugin/src/index.ts)。开启方式(见 example.js 头注释):
# 只看本插件的日志 DEBUG=puppeteer-extra-plugin:block-resources node example.js # 查看 puppeteer-extra 全家桶日志 DEBUG=puppeteer-extra,puppeteer-extra-plugin:* node example.js开启后会看到每次请求的裁决明细:onRequest { type: 'image', shouldBlock: true, alreadyHandled: false },以及每个页面创建时的onPageCreated { blockedTypes: ... },非常适合排查"为什么某个请求没被拦/被拦了"。
TypeScript 支持
插件随包附带类型声明 index.d.ts,TypeScript 用户开箱即用:
import blockResourcesPlugin from 'puppeteer-extra-plugin-block-resources' puppeteer.use( blockResourcesPlugin({ blockedTypes: new Set(['image', 'stylesheet']), interceptResolutionPriority: DEFAULT_INTERCEPT_RESOLUTION_PRIORITY }) )声明文件给出了完整的PluginOptions接口:availableTypes?: Set<ResourceType>、blockedTypes?: Set<ResourceType>、interceptResolutionPriority?: number,其中资源类型直接复用 puppeteer 导出的ResourceType联合类型,与运行时行为完全一致;Plugin类则暴露name、defaults、availableTypes、blockedTypes、interceptResolutionPriority等 getter 的类型签名。
生态组合:与 adblocker 等插件的配合
block-resources 是"按类型粗暴断流"的通用插件;而仓库中的 adblocker 插件(puppeteer-extra-plugin-adblocker)则基于 CLiqz 的 adblocker 引擎做基于规则的精细广告/跟踪器屏蔽。两者的典型分工是:
- 想彻底干掉图片、字体、媒体以极限提速 → 用 block-resources;
- 想精准屏蔽广告与跟踪器、同时保留图片和样式 → 用 adblocker;
- 两者可同时注册,此时通过
interceptResolutionPriority(adblocker 也支持该选项,见 packages/puppeteer-extra-plugin-adblocker/src/index.ts)协调拦截优先级即可。
由于它们都基于 puppeteer-extra 的插件生命周期(onPageCreated+request事件),组合使用时遵循同一套"协作式拦截"规则,不会互相覆盖裁决结果。
注意事项与限制
最后整理几个实操中值得留意的点:
- 拦截是全局生效于每个页面的:插件在
onPageCreated中对所有新页面开启拦截,如果你只想对特定页面生效,需要自行控制blockedTypes的增删时机; - 开启请求拦截会改变请求行为:
page.setRequestInterception(true)是 puppeteer 的全局开关,会让所有请求先经过 Node.js 侧事件循环,对高并发页面会有一定性能开销;屏蔽非必要资源带来的收益通常远大于此开销; - 动态修改是"进程内"行为:
blockedTypes.add(...)修改的是当前 Node.js 进程内插件实例的 Set,不影响已发出的请求,只对修改之后的请求生效; - 版本适配:插件拦截逻辑依赖 puppeteer 的协作式拦截 API(
isInterceptResolutionHandled、abortErrorReason、continueRequestOverrides、DEFAULT_INTERCEPT_RESOLUTION_PRIORITY),请在支持这些 API 的 puppeteer 版本上使用;包内 package.json 中puppeteer ^2.0.0仅为测试依赖,当前测试只校验插件类结构与默认值,并不依赖真实浏览器环境; - 屏蔽
document需谨慎:会直接中止页面主文档导航,一般只用于特殊场景(如拦截 iframe 内的文档请求)。
小结
puppeteer-extra-plugin-block-resources是一个"小而美"的请求拦截插件:API 只有构造函数、availableTypes、blockedTypes和interceptResolutionPriority四个入口,但借助 puppeteer 的请求拦截机制与 puppeteer-extra 的插件生命周期,实现了覆盖 13 种资源类型、支持运行时动态切换的完整拦截能力。无论是爬虫提速、数据节省还是页面净化,把它注册进 puppeteer-extra 后只需几行代码即可按类型精确控制浏览器加载哪些资源。
如果想深入阅读源码或动手验证,推荐按以下顺序查看仓库中的对应文件:
- 插件完整实现:packages/puppeteer-extra-plugin-block-resources/index.js
- 可运行示例:packages/puppeteer-extra-plugin-block-resources/example.js
- 单元测试:packages/puppeteer-extra-plugin-block-resources/index.test.js
- 类型声明:packages/puppeteer-extra-plugin-block-resources/index.d.ts
- 插件基类与生命周期:packages/puppeteer-extra-plugin/src/index.ts
- 插件注册与调度框架:packages/puppeteer-extra/src/index.ts
- 网页爬虫
- 浏览器控制
- 测试
【免费下载链接】puppeteer-extra
💯 Teach puppeteer new tricks through plugins.
相关推荐
Puppeteer ResourceType 资源类型完全解读:识别、过滤与拦截网络请求的基石
Puppeteer ResourceType 资源类型完全解读:识别、过滤与拦截网络请求的基石 在 Puppeteer 中,每一个由页面发出的 HTTP 请求都
浏览器控制测试网页爬虫开发工具提升Android应用交互体验:Bubbles for Android最佳实践与技巧
提升Android应用交互体验:Bubbles for Android最佳实践与技巧 Bubbles for Android是一个强大的Android库,为应用
Puppeteer ActionResult 类型详解:continue、abort、respond 三种请求拦截动作的实现与实战
Puppeteer ActionResult 类型详解:continue、abort、respond 三种请求拦截动作的实现与实战 导读 ActionResul
浏览器控制测试网页爬虫开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考