基于 puppeteer-extra-plugin-stealth 的 `_template` 模板开发自定义反检测插件实战指南

发布时间:2026/10/15 22:15:48

基于 puppeteer-extra-plugin-stealth 的 `_template` 模板开发自定义反检测插件实战指南 网页爬虫浏览器控制测试【免费下载链接】puppeteer-extra Teach puppeteer new tricks through plugins.项目地址https://gitcode.com/gh_mirrors/pu/puppeteer-extra点击查看免费下载本文以 packages/puppeteer-extra-plugin-stealth/evasions/_template 目录下的官方模板为起点讲解如何在 puppeteer-extra 体系中从零编写一个可独立运行的反检测 evasion 插件。读完本文你将掌握最小插件骨架的每一行代码、PuppeteerExtraPlugin基类的生命周期钩子、共享工具库_utils的正确用法并能参照navigator.webdriver、chrome.app等真实 evasion 的实现快速复制模板目录并开发出自己的新反检测方案。模板的本质一个最小的反检测插件骨架_template是puppeteer-extra-plugin-stealth官方提供的最小可运行插件模板官方对其定位非常直白见 模板 readmeMinimal stealth plugin template, not being used. :-) Feel free to copy this folder as the basis for additional detection evasion plugins.翻译过来就是这个模板本身并不参与实际反检测not being used它的用途是作为复制即用的起点——当你发现一个新的 headless 可探测特征、想为 stealth 插件贡献一条新 evasion 时直接复制整个_template文件夹改一改类名、插件名和onPageCreated里的注入逻辑即可。这也是 stealth 主插件官方建议的贡献路径主插件 index.js 的注释明确写着if you want to add a new evasion technique I suggest you look at the template to kickstart things。一个完整的 evasion 插件目录由三部分构成文件作用index.js插件本体继承PuppeteerExtraPlugin并实现生命周期钩子readme.md由documentation.js自动生成的 API 文档npm run docs会更新它package.json声明main: index.js与private: true见 模板 package.json目录结构可参照真实的navigator.pluginsevasionnavigator.plugins 目录除了index.js还按需附带mimeTypes.js、plugins.js、magicArray.js、data.json等拆分文件与配套测试。模板源码逐行拆解模板的完整实现位于 evasions/_template/index.js全文件仅 28 行结构如下use strict const { PuppeteerExtraPlugin } require(puppeteer-extra-plugin) /** * Minimal stealth plugin template, not being used. :-) * * Feel free to copy this folder as the basis for additional detection evasion plugins. */ class Plugin extends PuppeteerExtraPlugin { constructor(opts {}) { super(opts) } get name() { return stealth/evasions/_template } async onPageCreated(page) { await page.evaluateOnNewDocument(() { console.debug(hello world) }) } } module.exports function(pluginConfig) { return new Plugin(pluginConfig) }第一行严格模式use strict是 stealth 插件家族所有实现navigator.webdriver、chrome.app、navigator.plugins等的统一开头。由于这些代码最终会通过page.evaluateOnNewDocument注入浏览器页面上下文保持严格模式能避免一些隐蔽的静默错误。类的骨架与 opts 传递class Plugin extends PuppeteerExtraPlugin { constructor(opts {}) { super(opts) }PuppeteerExtraPlugin来自独立包puppeteer-extra-pluginstealth 的依赖声明见 package.json。它的构造函数会做一件关键的事把插件定义的defaults与用户传入的opts做深合并合并结果保存在this.opts中插件运行期通过this.opts.xxx读取配置见 src/index.ts 的_opts merge(this.defaults, opts || {})。name插件的全局唯一标识get name() { return stealth/evasions/_template }name是PuppeteerExtraPlugin基类要求每个插件必须覆写的属性基类默认实现会直接抛错Plugin must override name见 src/index.ts。它同时承担三个职责调试命名空间this.debug()的日志会以puppeteer-extra-plugin:${name}为前缀输出可用DEBUGpuppeteer-extra-plugin:插件名环境变量开关见 src/index.ts依赖解析stealth 主插件通过dependenciesgetter 把启用列表映射为stealth/evasions/名字形式的依赖名见 stealth index.jspuppeteer-extra会自动require()缺失的依赖见 src/index.ts事件绑定去重基类用_hasChildClassMember检查子类是否真的实现了某个钩子再决定是否注册对应的事件监听见 src/index.ts。命名惯例上真实 evasion 采用stealth/evasions/目录名格式例如stealth/evasions/navigator.webdriver、stealth/evasions/chrome.app。复制模板后第一件事就是把这里改成你的新 evasion 名称。onPageCreated反检测逻辑的注入点async onPageCreated(page) { await page.evaluateOnNewDocument(() { console.debug(hello world) }) }这是模板的灵魂所在。onPageCreated是基类提供的生命周期钩子之一当浏览器创建新页面包括window.open、browser.newPage、隐身上下文中的页面时被调用且已由基类预先过滤为page类型 target见 src/index.ts。基类通过监听 puppeteer 的targetcreated事件触发它。page.evaluateOnNewDocument保证注入脚本在页面任何脚本执行之前运行——这正是 stealth 反检测先下手为强的关键在页面自身的检测代码运行前先把navigator.webdriver、window.chrome等特征改写掉。真实插件对这一点利用得淋漓尽致例如navigator.webdriver/index.js 在onPageCreated中根据 Chrome 版本删除原型上的webdriver属性chrome.app/index.js 在 headless 下完整 mockwindow.chrome.appwindow.outerdimensions/index.js 补上 headless 缺失的window.outerWidth/outerHeight。模块导出工厂函数模式module.exports function(pluginConfig) { return new Plugin(pluginConfig) }模板采用工厂函数而不是直接导出类实例这是 stealth 插件家族的统一约定所有 evasion 的index.js末尾都是这个模式。这样做的原因在于opts的延迟注入调用方写成require(...)({ userAgent: ... })参数在实例化时才传入并参与defaults合并。默认参数opts {}在构造函数签名中已给出文档中标注opts (optional, default{})确保无参调用也能工作。生命周期钩子一个 evasion 能用到哪些时机PuppeteerExtraPlugin基类为插件提供了完整的一套钩子覆盖浏览器从启动到关闭的全过程全部为可选实现见 src/index.ts钩子触发时机典型用途beforeLaunch(options)新浏览器启动前修改 puppeteer 启动参数如追加--disable-blink-featuresAutomationControlledafterLaunch(browser, opts)浏览器启动后获取浏览器实例引用beforeConnect(options)puppeteer.connect()前修改连接参数afterConnect(browser, opts)连接建立后处理已连接浏览器onBrowser(browser, opts)launch/connect 任一方式拿到浏览器时统一处理两种来源onTargetCreated(target)新 target 创建处理任意类型 targetonPageCreated(page)新页面创建注入反检测脚本模板核心onTargetChanged(target)/onTargetDestroyed(target)target 变化/销毁状态清理onDisconnected()与浏览器断开清理资源onPluginRegistered()puppeteer.use()注册后初始化配套还有三个配置类 getter见 src/index.tsdefaults插件默认配置与用户 opts 深合并requirements声明launch不支持 connect、headful不支持 headless、dataFromPlugins、runLast等需求供基类向用户告警或调整执行顺序dependencies声明依赖的其他插件缺失时由puppeteer-extra自动加载。模板只用了onPageCreated这一个钩子但真实 evasion 常常组合多个。最典型的例子是 user-agent-override/index.js它同时实现了onPageCreated通过 CDP 的Network.setUserAgentOverride设置 UA 与平台、beforeLaunch记录是否 headless、beforeConnect把 connect 方式视为 headless以及data向user-preferences插件提供语言偏好数据还声明了dependencies: new Set([user-preferences])。使用共享工具库_utils让 mock 不留痕迹模板为了保持最小直接用了裸page.evaluateOnNewDocument。但 stealth 家族约 16 个 evasion完整清单见 stealth/index.js 的availableEvasions中大部分改写在技术上有一个共性痛点直接Object.defineProperty或new Proxy会留下可被反检测的蛛丝马迹例如Proxy 会出现在错误堆栈里at Reflect.get、at Object.get等行被替换函数的toString()结果不再是function xxx() { [native code] }新增属性会出现在Object.getOwnPropertyNames枚举中。puppeteer-extra-plugin-stealth/evasions/_utils目录正是为消除这些痕迹而生的共享工具箱_utils/index.js核心工具函数集合包括stripProxyFromErrors剥离 Proxy 错误堆栈、replaceProperty基于原有 descriptor 安全替换属性、patchToString/redirectToString伪造原生toString结果、replaceWithProxy/replaceGetterWithProxy/replaceGetterSetter/mockWithProxy/createProxy带反检测的 Proxy 包装、preloadCache/makeNativeString缓存原生toString字符串以防被二次嗅探、stringifyFns/materializeFns函数在 Node 与浏览器上下文间传递、memoize等_utils/withUtils.js对 page 的轻量封装提供page.evaluate和page.evaluateOnNewDocument的带工具版把_utils里的函数序列化后注入页面上下文页面内通过utils参数直接调用。一个使用withUtils的典型实现是 chrome.app/index.jsasync onPageCreated(page) { await withUtils(page).evaluateOnNewDocument(utils { // headless 下 window.chrome 不存在按 headful 的真实 descriptor 定义 Object.defineProperty(window, chrome, { writable: true, enumerable: true, configurable: false, value: {} }) // ... 构造 chrome.app 的静态数据与方法 mock utils.patchToStringNested(window.chrome.app) // 让所有 mock 函数看起来是原生代码 }) }navigator.pluginsevasionnavigator.plugins/index.js则展示了函数与数据分离的进阶写法把generateMimeTypeArray、generatePluginArray等生成函数用utils.stringifyFns序列化后随data来自data.json一起作为evaluateOnNewDocument的参数传入页面内再用utils.materializeFns还原——既保持了代码结构清晰又绕过了page.evaluate无法直接传函数的序列化限制。开发建议如果你的新 evasion 需要改写原生 API而不是像模板那样仅仅打印日志请务必复制_template后改用withUtils(page).evaluateOnNewDocument(...)模式并复用_utils中已有的工具避免能用但会被检测出来的半成品实现。实战如何把模板变成你自己的 evasion参照官方在 evasions/readme.md 中给出的指引If you want to add a new evasion technique I suggest you look at the template to kickstart things完整开发流程如下第 1 步复制模板目录把_template整个复制为evasions/你的新名字并同步修改package.json与readme.md。第 2 步改写插件名get name() { return stealth/evasions/你的新名字 }第 3 步实现反检测逻辑在onPageCreated中实现你的检测点修复。逻辑分两种形态直接修补类适合简单场景如 navigator.webdriver/index.jsasync onPageCreated(page) { await page.evaluateOnNewDocument(() { if (navigator.webdriver false) { // Post Chrome 89.0.4339.0 and already good } else if (navigator.webdriver undefined) { // Pre Chrome 89.0.4339.0 and already good } else { // Pre Chrome 88.0.4291.0 and needs patching delete Object.getPrototypeOf(navigator).webdriver } }) }该插件还在beforeLaunch中向启动参数追加--disable-blink-featuresAutomationControllednavigator.webdriver/index.js并注释了 Linux 下去掉 Chrome 警告 infobar 的 Chrome 策略配置方法。工具库辅助类适合 Proxy mock 场景如 media.codecs/index.js用utils.replaceWithProxy(HTMLMediaElement.prototype, canPlayType, canPlayType)拦截canPlayType调用对 Chromium 缺失的video/mp4; codecsavc1.42E01E、audio/x-m4a、audio/aac返回probably/maybe。第 4 步编写测试强烈建议stealth 插件的测试体系由 ava 驱动测试脚本见 package.json并提供了一组指纹采集工具 test/util.jsgetVanillaFingerPrint用原生 puppeteer 采集指纹、getStealthFingerPrint用addExtra(vanillaPuppeteer).use(Plugin())采集加了你的 evasion 之后的指纹两者对比即可验证效果。典型写法可参考 navigator.webdriver/index.test.js先断言 vanilla 下navigator.webdriver true再断言 stealth 下等于falseChrome ≥ 89.0.4339.0或undefined更早版本并附带回归测试确认不会误伤其他navigator方法。第 5 步独立使用或并入 stealth 主插件新 evasion 本身就是一个标准的puppeteer-extra插件可以独立使用const puppeteer require(puppeteer-extra) puppeteer.use(require(puppeteer-extra-plugin-stealth/evasions/你的新名字)())如果想并入 stealth 全家桶则需要两步见 stealth/index.js把新名字加入defaults中的availableEvasionsSet第 82-99 行enabledEvasions默认复制全部可用项第 103 行这样dependencies就会自动生成对应的依赖名并由puppeteer-extra动态加载第 112-116 行。常见问题与注意事项不要用page.setUserAgent()与 stealth 的 UA 处理混用user-agent-override插件注释明确警告常规的page.setUserAgent()会重置插件设置的语言与平台值见 user-agent-override/index.js。同理开发新 evasion 时要意识到 stealth 各插件之间是协同关系避免互相覆盖。evaluateOnNewDocument里不能直接用 Node 侧闭包变量需要通过withUtils的参数机制或stringifyFns序列化传递这是_utils存在的重要原因。保持模块化与最小依赖stealth 主插件的设计哲学是只加载启用的 evasion见 stealth/index.js 的 Modularity 说明。你的新插件应尽量自包含必要时才声明dependencies。documentation.js注释驱动文档readme.md由npm run docs内部执行documentation readme --shallow见 package.json从index.js的 JSDoc 注释自动生成。给类和方法写规范的 JSDocreadme.md的 API 段才会完整。版本与场景边界本仓库puppeteer-extra-plugin-stealth版本为 2.11.2引擎要求 Node 8见 package.json各 evasion 的实现细节如navigator.webdriver对不同 Chrome 版本的分支处理以源码注释为准实际效果随 Chrome 版本演进可能变化。总结_template虽只有 28 行代码却完整定义了 stealth evasion 插件的标准形态继承PuppeteerExtraPlugin、覆写name、在onPageCreated中通过evaluateOnNewDocument抢先注入补丁、以工厂函数导出。从它出发你可以对照_utils工具箱与navigator.webdriver、chrome.app、navigator.plugins、media.codecs等真实实现快速产出一个既模块化、又经得起指纹检测对比测试的新反检测插件——这正是 puppeteer-extra 社区持续扩展 stealth 能力的标准路径。赞分享网页爬虫浏览器控制测试【免费下载链接】puppeteer-extra Teach puppeteer new tricks through plugins.项目地址https://gitcode.com/gh_mirrors/pu/puppeteer-extra点击查看免费下载相关推荐puppeteer-extra-plugin-stealth 反检测插件解析navigator.hardwareConcurrency 硬件并发数伪装原理与实战puppeteer extra plugin stealth 反检测插件解析navigator.hardwareConcurrency 硬件并发数伪装原理与实网页爬虫浏览器控制测试puppeteer-extra-plugin-stealth 的 navigator.permissions 反检测插件原理、源码与测试全解析puppeteer extra plugin stealth 的 navigator.permissions 反检测插件原理、源码与测试全解析 在 puppe网页爬虫浏览器控制测试puppeteer-extra 插件解析chrome.loadTimes 反检测插件原理与实战puppeteer extra 插件解析chrome.loadTimes 反检测插件原理与实战 chrome.loadTimes 是 Chrome 专属的浏览网页爬虫浏览器控制测试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/15 22:15:48

2020-2021中国开发者调查报告:数据挖掘与工程化分析实践

简介:《2020-2021中国开发者调查报告》由CSDN官方出品,面向IT行业决策者、市场研究人员及广大开发者,系统呈现中国开发者群体的技术行为与行业趋势。报告基于CSDN自2004年积累的开发者调查数据,结合年度问卷重新设计,并…

2026/10/15 22:10:47

Flutter网络层请求校验与数据封装实践:用户中心My模块改造

做开源项目最怕什么?我自己的答案很直接:网络层写成一坨。这期笔记继续记录我在 Flutter 3.2.x 上维护的开源项目 OChina 的迭代过程,系列走到第 04 篇,聚焦主界面“My”模块里最容易被忽略、却直接影响体验的两件事——网络请求校…

2026/10/16 3:36:27

两数之和与哈希表:从暴力O(n²)到O(n)的思维转换

我第一次认认真真刷题,翻开的第一道就是“两数之和”。那时候觉得简单,两层 for 循环套上去,半小时写出代码,还挺得意。后来去面试,面试官把这道题原封不动出给我,我条件反射写了暴力解,他追问了…

2026/10/16 3:36:27

wget高级用法实战:断点续传、限速与递归镜像

前阵子在机房处理一个迁移任务,需要从内部对象存储拉下来一个将近 8GB 的压缩包,机器带宽明明有 1Gbps,可 wget 默认跑起来速度一直上不去,进度条纹丝不动,最后还因为 SSH 会话断开导致下载中断,又得从头再…

2026/10/16 3:36:27

SpringBoot2+Vue3政务管理系统开发实战与避坑指南

做政务管理系统这几年,我带过好几个类似的项目,从最早SSH那套老古董,到后来SpringBootVue前后端分离,踩过的坑可以写一本书。最近正好把一个完整的“Java Web政务管理系统”源码整理出来,技术栈是SpringBoot2 Vue3 M…

2026/10/16 3:36:27

Linux组调度与带宽控制:cgroup CPU限额实战

前几篇聊CFS的时候,我一直默认“调度单位是一个任务”。在桌面机上这个假设没问题,但把它搬到几十核的服务器上,尤其上面还跑着很多互相隔离的服务或容器时,问题就暴露得很明显:任务级公平并不等于组级公平。这篇文章专…

2026/10/16 3:31:27

分布式与集群的区别:架构设计选型核心指南

做架构设计这几年,不管是在团队内部评审、技术分享,还是面试候选人时,我总会被同一个问题反复问到:“分布式和集群到底有什么区别?”一开始我觉得只是个概念题,后来发现事情没那么简单。不少人简历上写着精…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/16 0:01:11

Cephadm实战指南:Linux系统工程师的生产级Ceph集群部署与排障

简介:本资源是一份完整的Ceph分布式存储系统中文技术文档合集,面向Linux系统运维工程师、云平台架构师及存储方向开发者,帮助读者系统掌握Ceph集群的部署、配置与运维核心能力。文档覆盖对象存储(RGW)、块设备&#xf…

2026/10/16 0:01:11

ITK中SpatialObject几何体系解析:坐标系、变换与管道集成

1. 空间对象的定位:为什么 ITK 需要一套几何描述体系很多刚开始接触 ITK 的朋友,包括我自己,最初都是被它的图像滤波、配准、分割这些“正统”功能吸引进来的。等真正上手做点实际项目,比如把分割出来的器官边界做量化分析、给血管…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑