☰
puppeteer-extra 插件 block-resources 实战:按资源类型动态拦截与放行网络请求
2026/10/15 11:34:00 网站建设 项目流程
  • 网页爬虫
  • 浏览器控制
  • 测试

【免费下载链接】puppeteer-extra

💯 Teach puppeteer new tricks through plugins.

项目地址:https://gitcode.com/gh_mirrors/pu/puppeteer-extra
点击查看免费下载

本文围绕 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 }))

这段代码做了三件事:

  1. require('puppeteer-extra-plugin-block-resources')拿到的是一个工厂函数,调用它(传入配置对象)返回插件实例;
  2. puppeteer.use(...)把插件注册进 puppeteer-extra,之后每次puppeteer.launch()创建的浏览器、每个新页面都会自动带上拦截逻辑;
  3. blockedTypes: new Set(['image', 'stylesheet'])声明屏蔽图片与样式表两种资源。

注册完成后,页面中的所有图片和 CSS 请求都会被直接中止(abort),页面 DOM 与脚本仍正常加载。

API 详解:一个构造函数、两个 Set、一个优先级

该插件的公开 API 非常精简,完整定义见 readme 与 TypeScript 声明文件 index.d.ts。

Plugin 构造函数

Plugin继承自PuppeteerExtraPlugin基类,类型为function (opts),接受一个可选配置对象(默认{}):

配置项类型默认值含义
opts.blockedTypesSet<string>空Set(默认不拦截任何类型)指定要屏蔽的 resourceTypes
opts.interceptResolutionPrioritynumberundefined协作式拦截的解析优先级(可选)

对应源码中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 页面本身)
stylesheetCSS 样式表
image图片资源
media音视频媒体(如<video>/<audio>源)
font字体文件(如 woff2、ttf)
scriptJavaScript 脚本
texttrack文本轨道(如视频字幕 VTT)
xhrXMLHttpRequest 异步请求
fetchFetch API 请求
eventsourceServer-Sent Events(EventSource 长连接)
websocketWebSocket 连接
manifestWeb 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') })()

这段代码演示了三个关键点:

  1. 保留插件实例引用:必须把blockResourcesPlugin实例存下来,才能在运行时操作它的blockedTypes;
  2. Set 的add/delete即改即生效:因为拦截判断每次请求时都会实时读取blockedTypes(见下文onRequest实现),所以无需重建浏览器或页面;
  3. 按页面切换屏蔽策略:同一浏览器进程内,访问不同站点时采用不同的拦截组合,比如访问 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)做了两件事:

  1. page.setRequestInterception(true):开启 puppeteer 的请求拦截开关,让页面的每个网络请求都以request事件的形式交给 Node.js 侧裁决;
  2. 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)可以拆成四步理解:

  1. 取类型:request.resourceType()拿到该请求的资源类型(即上面 13 种之一);
  2. 查集合:this.blockedTypes.has(type)判断它是否在当前屏蔽集合中——这里每次都实时读取,所以动态add/delete立即生效;
  3. 防重复处理:isInterceptResolutionHandled()是协作式拦截模式提供的查询 API,用于确认该请求是否已被其他拦截器处理过;若已被处理则直接返回,避免多个拦截器互相覆盖。从注释 "Requests are immediately handled if not using Cooperative Intercept Mode" 可以看出,在不具备该 API 的环境下插件选择保守跳过,防止多拦截器冲突;
  4. 裁决动作:
    • 命中屏蔽类型 →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),覆盖了四类断言:

  1. 导出形态:require('.')返回一个函数(工厂);
  2. 类结构完整性:实例名称是block-resources、具备_isPuppeteerExtraPlugin标记,原型上应恰好包含 8 个成员:constructor、name、defaults、availableTypes、blockedTypes、interceptResolutionPriority、onRequest、onPageCreated;
  3. 默认值: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事件),组合使用时遵循同一套"协作式拦截"规则,不会互相覆盖裁决结果。

注意事项与限制

最后整理几个实操中值得留意的点:

  1. 拦截是全局生效于每个页面的:插件在onPageCreated中对所有新页面开启拦截,如果你只想对特定页面生效,需要自行控制blockedTypes的增删时机;
  2. 开启请求拦截会改变请求行为:page.setRequestInterception(true)是 puppeteer 的全局开关,会让所有请求先经过 Node.js 侧事件循环,对高并发页面会有一定性能开销;屏蔽非必要资源带来的收益通常远大于此开销;
  3. 动态修改是"进程内"行为:blockedTypes.add(...)修改的是当前 Node.js 进程内插件实例的 Set,不影响已发出的请求,只对修改之后的请求生效;
  4. 版本适配:插件拦截逻辑依赖 puppeteer 的协作式拦截 API(isInterceptResolutionHandled、abortErrorReason、continueRequestOverrides、DEFAULT_INTERCEPT_RESOLUTION_PRIORITY),请在支持这些 API 的 puppeteer 版本上使用;包内 package.json 中puppeteer ^2.0.0仅为测试依赖,当前测试只校验插件类结构与默认值,并不依赖真实浏览器环境;
  5. 屏蔽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.

项目地址:https://gitcode.com/gh_mirrors/pu/puppeteer-extra
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询