- 网页爬虫
- 浏览器控制
- 测试
【免费下载链接】puppeteer-extra
💯 Teach puppeteer new tricks through plugins.
本文围绕 puppeteer-extra 官方文档展开,系统讲解这一基于 Puppeteer 的轻量级插件化包装器的安装、使用、公开 API 与底层实现原理。你将掌握如何通过
puppeteer.use()一键接入 stealth 反检测、广告拦截、验证码自动求解等插件,理解PuppeteerExtra类的插件生命周期(launch/connect 钩子、依赖解析、事件绑定),并学会基于PuppeteerExtraPlugin基类编写属于你自己的插件,以及通过addExtra()创建多个携带不同插件的独立实例。
项目定位:一个"教 Puppeteer 新技巧"的插件化增强层
puppeteer-extra是puppeteer(及其兼容实现)之上的一层轻量级包装器(light-weight wrapper),核心目标是"Teach puppeteer new tricks through plugins"——通过一套干净的接口让社区能以插件的形式为 Puppeteer 无限扩展能力。
它的最大价值在于:它是 Puppeteer 的直接替代品(drop-in replacement)。使用puppeteer-extra之后,原有所有 Puppeteer 代码无需任何改动即可继续工作,唯一的差别是你可以通过puppeteer.use()注册任意数量的插件,为浏览器注入额外行为。
从源码结构看,整个包的核心实现集中在 src/index.ts 中,仅依赖debug(调试日志)与deepmerge(选项深度合并)两个运行时依赖(见 package.json),保持了"轻量"的定位。它要求node >= 8,并把puppeteer、puppeteer-core、@types/puppeteer声明为可选的 peer 依赖(见 package.json)——这意味着你可以自行安装任意想用的 Puppeteer 版本,这正是其跨版本兼容性的基础。
安装:与任意 Puppeteer 版本搭配使用
yarn add puppeteer puppeteer-extra # - or - npm install puppeteer puppeteer-extra # puppeteer-extra works with any puppeteer version: yarn add puppeteer@2.0.0 puppeteer-extrapuppeteer是puppeteer-extra的 peer 依赖,官方文档明确说明它兼容任何版本的 puppeteer。如果你只需要puppeteer-core(不带内置 Chromium 的版本),同样可以安装,默认导出会依次尝试require('puppeteer')、require('puppeteer-core')(详见 requireVanillaPuppeteer)。
如果项目里没有安装任何 puppeteer,puppeteer-extra不会在导入时报错,而是在首次真正调用浏览器相关 API 时(如launch()),通过 pptr getter 抛出No puppeteer instance provided.错误,并在控制台提示你安装puppeteer或puppeteer-core。这一点在 addExtra.ts 测试中得到了验证。
快速上手:两行代码接入 stealth 与 adblocker 插件
官方文档给出的 Quickstart 展示了完整的插件化用法——先注册插件,之后的 Puppeteer 使用方式与平时完全一致:
// puppeteer-extra is a drop-in replacement for puppeteer, // it augments the installed puppeteer with plugin functionality. // Any number of plugins can be added through `puppeteer.use()` const puppeteer = require('puppeteer-extra') // Add stealth plugin and use defaults (all tricks to hide puppeteer usage) const StealthPlugin = require('puppeteer-extra-plugin-stealth') puppeteer.use(StealthPlugin()) // Add adblocker plugin to block all ads and trackers (saves bandwidth) const AdblockerPlugin = require('puppeteer-extra-plugin-adblocker') puppeteer.use(AdblockerPlugin({ blockTrackers: true })) // That's it, the rest is puppeteer usage as normal 😊 puppeteer.launch({ headless: true }).then(async browser => { const page = await browser.newPage() await page.setViewport({ width: 800, height: 600 }) console.log(`Testing adblocker plugin..`) await page.goto('https://www.vanityfair.com') await page.waitForTimeout(1000) await page.screenshot({ path: 'adblocker.png', fullPage: true }) console.log(`Testing the stealth plugin..`) await page.goto('https://bot.sannysoft.com') await page.waitForTimeout(5000) await page.screenshot({ path: 'stealth.png', fullPage: true }) console.log(`All done, check the screenshots. ✨`) await browser.close() })上面的示例用到了stealth和adblocker两个插件,它们需要单独安装:
yarn add puppeteer-extra-plugin-stealth puppeteer-extra-plugin-adblocker # - or - npm install puppeteer-extra-plugin-stealth puppeteer-extra-plugin-adblocker如果想要查看插件内部的调试输出,只需要在运行脚本时设置DEBUG环境变量:
DEBUG=puppeteer-extra,puppeteer-extra-plugin:* node myscript.jspuppeteer-extra与每个插件都基于debug模块输出日志:核心库使用puppeteer-extra命名空间,插件基类则按puppeteer-extra-plugin:<plugin_name>自动命名(见 插件基类的 debug getter)。该日志机制在核心的 index.ts 与各插件源码中随处可见,例如注册插件时会输出plugin registered日志。
PuppeteerExtra 公开 API 全解
puppeteer-extra的核心类是PuppeteerExtra,官方文档(由 JSDoc 自动生成)将其定义为"模块化插件框架,用于教 puppeteer 新技巧",并明确指出该模块充当 puppeteer 的 drop-in replacement。以下逐一说明每个公开成员。
class: PuppeteerExtra
.use(plugin)
pluginPuppeteerExtraPlugin- 返回:this(同一个
PuppeteerExtra实例,支持链式调用)
这是注册插件的主入口接口,也是整个框架最重要的方法。官方示例:
puppeteer.use(plugin1).use(plugin2)从源码看,use()会做三重校验与初始化(见 use 实现):
- 校验插件是否为合法对象且带有
_isPuppeteerExtraPlugin标记,否则打印警告并忽略; - 校验插件是否提供了
name,没有名字的插件会被忽略; - 若插件声明了
dataFromPlugins需求,则把getPluginData绑定为插件的getDataFromPlugins方法;随后调用插件的_register()(内部会触发onPluginRegistered钩子)并把插件推入内部数组_plugins。
测试 basic.ts 验证了puppeteer.use是函数、puppeteer.plugins是数组等公开成员的存在性。
.launch(options?)
optionsPuppeteer.LaunchOptions?(参考 puppeteer 官方 launch 选项)- 返回:Promise<Puppeteer.Browser>
启动浏览器实例,浏览器会在父 Node.js 进程关闭时随之关闭。该方法在原始puppeteer.launch之上增强了插件生命周期能力:所有注册了beforeLaunch方法的插件会被按顺序调用,有机会在启动前修改options对象(见 launch 实现)。
const browser = await puppeteer.launch({ headless: false, defaultViewport: null })launch内部的执行流程非常值得关注(我们将在"源码级解析"一节深入展开):
- 用
deepmerge合并默认启动选项{ args: [] },保证options.args数组一定存在; - 解析插件依赖(
resolvePluginDependencies)并对插件排序(orderPlugins); - 以"瀑布流"方式逐个调用插件的
beforeLaunch,允许每个插件修改并回传options; - 检查插件需求(
checkPluginRequirements),例如 headless 模式不兼容警告; - 调用底层 puppeteer 的
launch,并对浏览器实例打补丁(_patchPageCreationMethods); - 调用所有插件的
_bindBrowserEvents,把targetcreated、targetchanged等浏览器事件绑定到插件方法上。
.connect(options?)
optionsPuppeteer.ConnectOptions?(参考 puppeteer 官方 connect 选项)- 返回:Promise<Puppeteer.Browser>
将 Puppeteer 附加到一个已存在的 Chromium 实例。与launch对称,它会增强原始puppeteer.connect:所有注册了beforeConnect方法的插件会被按顺序调用,有机会修改连接选项(见 connect 实现)。launch与connect的生命周期差异在测试 events.js 中有完整覆盖:connect场景下会触发beforeConnect/afterConnect/onBrowser等钩子,而不会触发beforeLaunch/afterLaunch/onClose。
.defaultArgs(options?)
optionsPuppeteer.ChromeArgOptions?- 返回:Array<string>
返回 Chromium 启动时的默认 flags(命令行参数列表),直接透传给底层 puppeteer 的同名方法(见 defaultArgs 实现)。
.executablePath()
- 返回:string
Puppeteer 期望找到内置 Chromium 的路径(见 executablePath 实现)。
.createBrowserFetcher(options?)
optionsPuppeteer.FetcherOptions?- 返回:Puppeteer.BrowserFetcher
创建浏览器下载器(BrowserFetcher),用于下载指定版本的 Chromium,同样直接透传底层实现(见 createBrowserFetcher 实现)。
.plugins
- 类型:Array<PuppeteerExtraPlugin>
获取所有已注册插件的列表(见 plugins getter)。测试 plugin-support.js 验证了puppeteer.plugins.length与插件name的正确性。
.getPluginData(name?)
namestring?(可选的插件名过滤条件)- 返回:所有插件暴露的
data属性经过**摊平(flatten)**后的单个数组;传入name时仅返回匹配项
该方法主要服务于需要从其他插件获取数据的插件(例如user-preferences插件需要收集其他插件提供的偏好设置)。在use()注册时,声明了dataFromPlugins需求的插件会获得getPluginData的绑定(见 getPluginData 实现)。测试 plugin-support.js 验证了全量获取、按名过滤与空结果三种场景。
defaultExport()
- 类型:PuppeteerExtra
默认导出的行为与常规 puppeteer 完全一致(只是多了插件功能),可直接作为 drop-in replacement 使用。其背后会尝试从已安装的依赖中require出puppeteer或puppeteer-core。
// javascript import const puppeteer = require('puppeteer-extra') // typescript/es6 module import import puppeteer from 'puppeteer-extra' // Add plugins puppeteer.use(...)注意默认导出是延迟解析底层 puppeteer 的(见 defaultExport 实现),这样即使你打算使用非标准 puppeteer 实现(通过addExtra),也不会在导入阶段就抛错。
addExtra(puppeteer)
puppeteerVanillaPuppeteer(任意与 puppeteer API 兼容的实现或版本)- 返回:PuppeteerExtra(一个使用给定 puppeteer 的全新实例)
这是另一种使用方式:为传入的 puppeteer 增加插件能力。它特别适用于两种场景:
- 需要多个带不同插件的 puppeteer 实例;
- 需要为非标准的 puppeteer 包(如
puppeteer-firefox、chrome-aws-lambda内置的 puppeteer)添加插件。
// js import const puppeteerVanilla = require('puppeteer') const { addExtra } = require('puppeteer-extra') // ts/es6 import import puppeteerVanilla from 'puppeteer' import { addExtra } from 'puppeteer-extra' // Patch provided puppeteer and add plugins const puppeteer = addExtra(puppeteerVanilla) puppeteer.use(...)addExtra的源码实现非常简单——new PuppeteerExtra(puppeteer)(见 addExtra 实现),其行为由 addExtra.ts 测试覆盖:传入null时会返回一个PuppeteerExtra实例,但在访问pptr时会抛出 "No puppeteer instance provided." 错误。
源码级解析:插件是如何"教会" Puppeteer 新技巧的
要真正用好puppeteer-extra,理解 src/index.ts 中PuppeteerExtra类的几个私有机制至关重要。
1. 插件注册校验:use()的双保险
use()要求插件必须是带有_isPuppeteerExtraPlugin标记的对象且必须提供name,否则会被忽略并打印警告。这一设计保证了注册进框架的一定是合格的插件,避免非法对象污染插件列表(见 use 实现)。
2. launch/connect 的完整生命周期
以launch为例,其执行顺序为:merge 默认选项 → resolvePluginDependencies → orderPlugins → beforeLaunch 瀑布流 → checkPluginRequirements → 底层 launch → _patchPageCreationMethods → _bindBrowserEvents。
其中beforeLaunch采用"瀑布流"(waterfall)调用:插件可以修改传入的options或返回更新后的值,后一个插件收到的是前一个插件改过的结果(见 callPluginsWithValue 实现)。测试 options.js 验证了这一机制:插件在beforeLaunch中向options.args追加--foobar=true、修改timeout与headless,最终afterLaunch收到的opts.options与预期完全一致。
3.about:blank页面创建补丁
这是一个非常有意思的实现细节。源码注释指出:Puppeteer 的targetcreated事件有时触发不够早,导致监听onPageCreated的插件来不及在页面发起首个请求前修改页面(例如设置 user-agent)。作为变通方案,_patchPageCreationMethods会包装浏览器的_createPageInContext方法,在每次创建页面后立刻再导航一次about:blank,从而可靠地解决首请求时机问题,且几乎没有可感知的延迟或副作用(见 补丁实现)。该补丁只覆盖显式创建的页面,通过window.open隐式创建的页面仍受此问题影响。
4. 插件依赖的自动解析
插件可以在dependencies(一个Set)中声明依赖。resolvePluginDependencies会收集所有缺失的依赖,按puppeteer-extra-plugin-<name>命名约定自动require()并注册(见 resolvePluginDependencies 实现)。这意味着用户无需手动 require 依赖插件,除非想修改它的默认配置;若依赖未安装,控制台会给出yarn add <packageName>的安装提示。例如 user-preferences 插件 声明了user-data-dir依赖,使用它时框架会自动加载puppeteer-extra-plugin-user-data-dir。
5. 插件排序与需求检查
orderPlugins:将声明了runLast需求的插件移到列表末尾,便于那些依赖其他插件数据的插件最后执行(见 orderPlugins 实现)。checkPluginRequirements:在 launch/connect 前检查需求——launch上下文中若插件声明headful而用户以headless: true启动,会警告"该插件不支持 headless 模式";connect上下文中若插件声明launch需求,会警告"该插件不支持 puppeteer.connect()"(见 checkPluginRequirements 实现)。
PuppeteerExtraPlugin 基类:编写你自己的第一个插件
插件的编写规范由puppeteer-extra-plugin包中的PuppeteerExtraPlugin抽象基类定义(见 基类源码)。官方文档特别指出,可以参照 anonymize-ua 插件 这个"最小示例"来学习。
插件必须/可选实现的核心成员
| 成员 | 是否必填 | 说明 |
|---|---|---|
get name() | 必填 | 插件名,约定包名为puppeteer-extra-plugin-<name>、插件名为<name>,未实现会直接抛错 |
get defaults() | 可选 | 默认配置对象,会与用户传入的选项做深合并,合并结果通过this.opts访问 |
get requirements() | 可选 | 需求集合,支持launch、headful、dataFromPlugins、runLast |
get dependencies() | 可选 | 依赖插件名集合,缺失时由puppeteer-extra自动加载 |
get data() | 可选 | 暴露给其他插件的数据(约定[{ name, value }]结构) |
get opts | 只读 | 合并后的插件配置 |
get debug | 只读 | 基于debug模块、自动按插件名命名空间的日志器 |
生命周期钩子方法
基类把所有常见 puppeteer 浏览器事件自动绑定到同名的插件方法上(前提是插件确实实现了该方法,基类会通过原型链检查来避免多余监听,见 _bindBrowserEvents 实现):
| 钩子方法 | 触发时机 | 典型用途 |
|---|---|---|
beforeLaunch(options) | 浏览器启动前 | 修改 launch 选项,如追加--ppapi-flash-path |
afterLaunch(browser, opts) | 浏览器启动后 | 确认启动结果、读取最终选项 |
beforeConnect(options) | 连接已有实例前 | 修改 connect 选项 |
afterConnect(browser, opts) | 连接成功后 | 连接场景下的收尾处理 |
onBrowser(browser, opts) | 任意浏览器实例可用时(launch 与 connect 都触发) | 不关心实例来源的统一处理 |
onTargetCreated(target) | 目标创建时(含隐身上下文) | 监听新页面/新 target |
onPageCreated(page) | 新页面创建时(已按 page 类型过滤) | 修改页面 UA、注入脚本等 |
onTargetChanged(target) | target URL 变化时 | 响应导航 |
onTargetDestroyed(target) | target 销毁时 | 清理资源 |
onDisconnected(target) | Puppeteer 与 Chromium 断开时 | 统一退出清理(puppeteer ≥ 1.6.0 推荐) |
onClose() | 进程退出/断开时(仅 launch 场景,可能多次触发) | 旧版兼容,清理临时文件 |
onPluginRegistered() | puppeteer.use()注册后 | 注册时机初始化 |
事件绑定流程在测试 events.js 中有完整验证:launch 场景会依次触发beforeLaunch → afterLaunch → onBrowser → onTargetCreated → onPageCreated → onTargetChanged → onTargetDestroyed → onDisconnected → onClose,而 connect 场景只触发beforeConnect/afterConnect且不触发onClose。
一个可运行的 hello-world 插件
参照基类文档示例,编写插件需要两步:定义继承PuppeteerExtraPlugin的类,并以工厂函数导出(这样用户可以通过require(...)()实例化):
// hello-world-plugin.js const { PuppeteerExtraPlugin } = require('puppeteer-extra-plugin') class Plugin extends PuppeteerExtraPlugin { constructor (opts = { }) { super(opts) } get name () { return 'hello-world' } async onPageCreated (page) { this.debug('page created', page.url()) const ua = await page.browser().userAgent() this.debug('user agent', ua) } } module.exports = function (pluginConfig) { return new Plugin(pluginConfig) }使用它:
// foo.js const puppeteer = require('puppeteer-extra') puppeteer.use(require('./hello-world-plugin')()) ;(async () => { const browser = await puppeteer.launch({headless: false}) const page = await browser.newPage() await page.goto('http://example.com', {waitUntil: 'domcontentloaded'}) await browser.close() })()再看真实的 anonymize-ua 插件,它演示了defaults与选项合并的完整模式:默认配置{ stripHeadless: true, makeWindows: true, customFn: null },用户在实例化时可以覆盖,例如puppeteer.use(require('puppeteer-extra-plugin-anonymize-ua')({ customFn: (ua) => 'MyCoolAgent/' + ua.replace('Chrome', 'Beer') }))。其onPageCreated中分别处理了去HeadlessChrome标记、伪装 Windows 平台、应用自定义 UA 替换函数三种逻辑。
多实例与场景化集成
场景一:多个携带不同插件的 puppeteer 实例
通过addExtra可以在同一个进程里创建多个彼此隔离的实例,各自注册不同的插件(或相同插件的不同配置):
const vanillaPuppeteer = require('puppeteer') const { addExtra } = require('puppeteer-extra') const AnonymizeUA = require('puppeteer-extra-plugin-anonymize-ua') async function main() { const pptr1 = addExtra(vanillaPuppeteer) pptr1.use( AnonymizeUA({ customFn: ua => 'Hello1/' + ua.replace('Chrome', 'Beer') }) ) const pptr2 = addExtra(vanillaPuppeteer) pptr2.use( AnonymizeUA({ customFn: ua => 'Hello2/' + ua.replace('Chrome', 'Beer') }) ) await checkUserAgent(pptr1) await checkUserAgent(pptr2) } main() async function checkUserAgent(pptr) { const browser = await pptr.launch({ headless: true }) const page = await browser.newPage() await page.goto('https://httpbin.org/headers', { waitUntil: 'domcontentloaded' }) const content = await page.content() console.log(content) await browser.close() }场景二:与 puppeteer-cluster 集群配合
puppeteer-cluster允许创建一组 puppeteer 工作进程,与puppeteer-extra配合良好。关键点在于用addExtra构造一个自定义实例后,直接作为Cluster.launch({ puppeteer })的puppeteer参数传入:
const { Cluster } = require('puppeteer-cluster') const vanillaPuppeteer = require('puppeteer') const { addExtra } = require('puppeteer-extra') const Stealth = require('puppeteer-extra-plugin-stealth') const Recaptcha = require('puppeteer-extra-plugin-recaptcha') async function main() { // Create a custom puppeteer-extra instance using `addExtra`, // so we could create additional ones with different plugin config. const puppeteer = addExtra(vanillaPuppeteer) puppeteer.use(Stealth()) puppeteer.use(Recaptcha()) // Launch cluster with puppeteer-extra const cluster = await Cluster.launch({ puppeteer, maxConcurrency: 2, concurrency: Cluster.CONCURRENCY_CONTEXT }) // Define task handler await cluster.task(async ({ page, data: url }) => { await page.goto(url) const { hostname } = new URL(url) const { captchas } = await page.findRecaptchas() console.log(`Found ${captchas.length} captcha on ${hostname}`) await page.screenshot({ path: `${hostname}.png`, fullPage: true }) }) // Queue any number of tasks cluster.queue('https://bot.sannysoft.com') cluster.queue('https://www.google.com/recaptcha/api2/demo') cluster.queue('http://www.wikipedia.org/') await cluster.idle() await cluster.close() console.log(`All done, check the screenshots. ✨`) } // Let's go main().catch(console.warn)TypeScript 下的导入方式对应为:
import { Cluster } from 'puppeteer-cluster' import vanillaPuppeteer from 'puppeteer' import { addExtra } from 'puppeteer-extra' import Stealth from 'puppeteer-extra-plugin-stealth' import Recaptcha from 'puppeteer-extra-plugin-recaptcha'场景三:与 chrome-aws-lambda 配合(Serverless)
在 AWS Lambda 等 Serverless 场景使用chrome-aws-lambda时,需要把其内置的 puppeteer 通过addExtra包装。文档特别提醒:如果与stealth插件一起使用,需要修改默认启动参数以移除--disable-notifications标志,才能通过全部测试:
const chromium = require('chrome-aws-lambda') const { addExtra } = require('puppeteer-extra') const puppeteerExtra = addExtra(chromium.puppeteer) const launch = async () => { puppeteerExtra .launch({ args: chromium.args, defaultViewport: chromium.defaultViewport, executablePath: await chromium.executablePath, headless: chromium.headless }) .then(async browser => { const page = await browser.newPage() await page.goto('https://www.spacejam.com/archive/spacejam/movie/jam.htm') await page.waitForTimeout(10 * 1000) await browser.close() }) } launch() // Launch Browser场景四:与 browserless 配合
browserless是一个面向 Headless 生产环境的 puppeteer 类 Node.js 库。将puppeteer-extra实例直接传入即可获得插件能力:
const puppeteer = require('puppeteer-extra') const StealthPlugin = require('puppeteer-extra-plugin-stealth') puppeteer.use(StealthPlugin()) const browserless = require('browserless')({ puppeteer }) const saveBufferToFile = (buffer, fileName) => { const wstream = require('fs').createWriteStream(fileName) wstream.write(buffer) wstream.end() } browserless .screenshot('https://bot.sannysoft.com', { device: 'iPhone 6' }) .then(buffer => { const fileName = 'screenshot.png' saveBufferToFile(buffer, fileName) console.log(`your screenshot is here: `, fileName) })TypeScript 使用
puppeteer-extra核心及大部分插件均由 TypeScript 编写,开箱即用地提供了完整的类型支持与 VSCode 自动补全。官方文档给出的 TS 用法:
import puppeteer from 'puppeteer-extra' import AdblockerPlugin from 'puppeteer-extra-plugin-adblocker' import StealthPlugin from 'puppeteer-extra-plugin-stealth' puppeteer.use(AdblockerPlugin()).use(StealthPlugin()) puppeteer .launch({ headless: false, defaultViewport: null }) .then(async browser => { const page = await browser.newPage() await page.goto('https://bot.sannysoft.com') await page.waitForTimeout(5000) await page.screenshot({ path: 'stealth.png', fullPage: true }) await browser.close() })值得注意的是类型兼容细节:Puppeteer 在 v6 起从@types/puppeteer切换为内置类型。仓库为此提供了 puppeteer-legacy.d.ts 兼容层——当使用 puppeteer < v6 时,它会把VanillaPuppeteer接口合并进puppeteer/puppeteer-core的模块声明中(文件头部通过@ts-nocheck确保在 pptr ≥ v6 时不会引发类型错误)。
另外,Playwright 用户可以使用playwright-extra(puppeteer-extra的姊妹包),它以相同的方式为 Playwright 提供插件支持。
官方与社区插件生态一览
官方文档将仓库内维护的插件分为两类:官方插件与社区贡献插件。以下官方插件全部位于 packages 目录 下。
官方插件
| 插件 | 核心能力 |
|---|---|
puppeteer-extra-plugin-stealth | 应用多种规避(evasion)技术,让 puppeteer 更难以被检测;支持 Chromium/Chrome 的 headless 与 headful 模式 |
puppeteer-extra-plugin-recaptcha | 一行代码page.solveRecaptchas()自动求解 reCAPTCHA 与 hCaptcha |
puppeteer-extra-plugin-adblocker | 快速高效的广告与追踪器拦截器,节省带宽、降低加载时间;支持blockTrackers、blockTrackersAndAnnoyances、useCache等选项 |
puppeteer-extra-plugin-devtools | 创建安全隧道,让 DevTools 前端(含屏幕录制 screencasting)从公网可访问,实现任意位置调试 |
puppeteer-extra-plugin-repl | 提供交互式 REPL,让快速的 puppeteer 调试与探索变得有趣 |
puppeteer-extra-plugin-block-resources | 在 puppeteer 中拦截资源(图片、媒体、CSS 等),支持全部资源类型且可动态开关 |
puppeteer-extra-plugin-flash | 允许 Flash 内容在所有站点无需用户交互即可运行 |
puppeteer-extra-plugin-anonymize-ua | 在所有页面匿名化 User-Agent,支持动态替换,保证浏览器版本保持最新 |
puppeteer-extra-plugin-user-preferences | 允许设置自定义 Chrome/Chromium 用户偏好,自身也暴露插件接口,供其他插件(如puppeteer-extra-plugin-font-size)使用 |
以user-preferences为例可以直观看到插件间的数据协作模式:它声明了runLast与dataFromPlugins需求(见 index.js),在beforeLaunch中通过this.getDataFromPlugins('userPreferences')收集其他插件暴露的偏好并做深度合并,最终以userDataDirFile数据的形式把Preferences文件写入用户数据目录。
社区插件
以下插件由社区成员慷慨贡献,托管在项目仓库之外,不在主项目的控制与监督范围内:
- puppeteer-extra-plugin-minmax:实时最小化/最大化 puppeteer,非常适合手动解决验证码。
- puppeteer-extra-plugin-portal:利用 Chromium 的 screencast API 远程查看并交互 puppeteer 会话,适合在自动化任务卡住(如验证码)时远程人工介入。
更多插件可查看 packages 文件夹;如果你想亲自贡献一个插件,可参考
puppeteer-extra-plugin基类文档与 anonymize-ua 插件 这个最小示例。
测试与质量保障
仓库对puppeteer-extra的测试位于 test 目录 下,采用 ava 测试框架,分为 TS(ava.config-ts.js)与 JS(ava.config.js)两套配置:
| 测试文件 | 覆盖内容 |
|---|---|
| basic.ts | 默认导出是PuppeteerExtra实例、公开/内部成员存在性、原生 puppeteer 成员透传 |
| addExtra.ts | addExtra返回PuppeteerExtra实例、无 puppeteer 时抛错 |
| options.js | 插件通过beforeLaunch/beforeConnect修改启动/连接选项的瀑布流机制 |
| events.js | launch/connect 两种场景下插件生命周期事件与浏览器事件绑定的完整性 |
| connect.js | 通过connect连接已有实例时插件仍然生效(如 UA 匿名化) |
| plugin-support.js | 插件注册、plugins/pluginNames/getPluginData的读取 |
官方文档还声明:puppeteer-extra与所有插件在 NodeJS 稳定版与 LTS 版、以及一系列当前 puppeteer 版本的矩阵中持续测试,且从未破坏过兼容性,仍支持早至 2018 年的早期 puppeteer 版本。
版本演进与使用注意事项
从官方 Changelog 可知,puppeteer-extra在2.1.6 ➠ 3.1.1之间经历了一次大重构:核心改为 TypeScript 编写,为 TS 用户提供开箱即用的类型安全,也为 JS 用户提供 VSCode 自动补全;同时新增了addExtra导出,用于为任意 puppeteer 兼容库补齐插件功能(如chrome-aws-lambda),并支持多个携带不同插件的实例。API 完全向后兼容,只是主版本号做了递增以防万一。
实际使用中还有几点需要注意:
- headless 限制:部分插件在
headless: true模式下不工作(文档会在对应插件处注明),例如user-preferences插件,这是 Chrome 本身的限制;如果这些场景仍需要无头体验,可借助xvfb-run。 - peer 依赖缺失:未安装 puppeteer 时,
launch()等调用会抛出 "No puppeteer instance provided." 错误,控制台会给出安装指引。 - 调试手段:通过
DEBUG=puppeteer-extra,puppeteer-extra-plugin:*可以观察插件注册、选项修改、依赖解析等全部内部流程,是排查插件问题的第一利器。
puppeteer-extra以 MIT 许可证开源(Copyright © 2018 - 2023, berstend),核心包声明为node >= 8。无论你是想给爬虫加上 stealth 反检测、接入广告拦截与验证码求解,还是希望为 Puppeteer 生态贡献一个自己的插件,这套轻量而设计清晰的插件框架都值得作为基础设施纳入你的技术栈。
- 网页爬虫
- 浏览器控制
- 测试
【免费下载链接】puppeteer-extra
💯 Teach puppeteer new tricks through plugins.
相关推荐
NZBGet入门教程:从安装到下载的快速上手指南
NZBGet入门教程:从安装到下载的快速上手指南 NZBGet是一款高效的Usenet下载工具,采用C++开发,以性能和效率著称,可运行在PC、NAS、媒体播放
后端puppeteer-extra-plugin-repl 插件深度实战:为 Puppeteer 调试打造交互式 REPL
puppeteer extra plugin repl 插件深度实战:为 Puppeteer 调试打造交互式 REPL 导读 puppeteer extra p
网页爬虫浏览器控制测试puppeteer-extra浏览器指纹伪装技术:webgl.vendor与navigator插件应用
puppeteer extra浏览器指纹伪装技术:webgl.vendor与navigator插件应用 引言:浏览器指纹追踪的隐形威胁 你是否曾疑惑为何刚浏览某
网页爬虫浏览器控制测试
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考