
网页爬虫浏览器控制测试【免费下载链接】puppeteer-extra Teach puppeteer new tricks through plugins.项目地址https://gitcode.com/gh_mirrors/pu/puppeteer-extra点击查看免费下载本文围绕puppeteer-extra-plugin-stealth插件包中navigator.hardwareConcurrencyevasion 的 API 文档readme.md展开结合源码与测试用例讲解该 evasion 如何把浏览器暴露的navigator.hardwareConcurrency固定为 4可配置以及为什么这能降低无头浏览器被指纹采集脚本识别的概率。读完本文你将掌握该插件的配置参数、单独/组合启用方式、底层 Proxy 实现原理以及如何用仓库自带的测试验证伪装效果。一、为什么硬件并发数会成为反检测的暴露点navigator.hardwareConcurrency是 Web 标准暴露的一个只读属性用于告知页面当前设备的逻辑 CPU 核心数量。在真实浏览器中它的取值直接来自操作系统信息例如一个 8 核 CPU 的机器返回8。但对于无头headless浏览器而言这个值在部分环境下会与宿主机器产生不匹配或者在不同操作系统/浏览器组合下呈现出异常的分布规律从而被指纹采集类脚本例如仓库测试中使用的fpcollect、fpscanner纳入判别维度。原文档中对此的定位非常简洁Set the hardwareConcurrency to 4 (optionally configurable withhardwareConcurrency)即把navigator.hardwareConcurrency固定设置为 4可以通过hardwareConcurrency参数自定义从而让该指纹特征落在一个看起来更常见的取值上。原文档还附带了一条指向公开统计数据报告关于操作系统与 hardwareConcurrency 取值分布的参考链接用于佐证 4 是一个合理的默认值。二、插件 API 与配置参数该 evasion 是一个标准的puppeteer-extra插件API 文档定义如下完整继承自 readme.mdclass: PluginoptsObject?可选配置项默认{}opts.hardwareConcurrencynumber?用于navigator.hardwareConcurrency的取值默认4继承自: PuppeteerExtraPlugin核心功能将navigator.hardwareConcurrency设置为 4可通过hardwareConcurrency选项自定义。参数默认值的来源defaults 机制在 index.js 中插件通过defaultsgetter 声明默认值get defaults() { return { hardwareConcurrency: 4 } }这里依托的是puppeteer-extra-plugin基类提供的 options 合并机制基类在构造时会执行this._opts merge(this.defaults, opts || {})见 packages/puppeteer-extra-plugin/src/index.ts将用户传入的配置与defaults做深合并合并结果统一通过this.opts访问。也就是说不传任何参数 →hardwareConcurrency取默认值4传入{ hardwareConcurrency: 8 }→ 插件内的opts.hardwareConcurrency即为8。三、快速上手两种启用方式方式一随 stealth 全家桶默认启用navigator.hardwareConcurrency是 stealth 主插件默认启用的 evasion 之一。在 stealth 主插件 的availableEvasions集合中可以看到它且默认enabledEvasions为全部 evasionconst puppeteer require(puppeteer-extra) const StealthPlugin require(puppeteer-extra-plugin-stealth) puppeteer.use(StealthPlugin()) puppeteer.launch({ headless: true }).then(async browser { const page await browser.newPage() await page.goto(https://example.com) const cores await page.evaluate(() navigator.hardwareConcurrency) console.log(cores) // 4默认值 await browser.close() })安装方式仓库 stealth readmenpm install puppeteer puppeteer-extra puppeteer-extra-plugin-stealth # 或 yarn add puppeteer puppeteer-extra puppeteer-extra-plugin-stealth方式二单独启用该 evasionstealth 插件包采用模块化设计每个 evasion 目录都是独立的puppeteer-extra插件可以直接绕过主模块单独加载const puppeteer require(puppeteer-extra) // 只启用 hardwareConcurrency 伪装并自定义取值为 8 puppeteer.use( require(puppeteer-extra-plugin-stealth/evasions/navigator.hardwareConcurrency)({ hardwareConcurrency: 8 }) )这与主插件在 index.js 中声明的方式一致——stealth 主插件本身就是通过dependencies把各个 evasion 按名称动态 require 并组合起来的因此两种方式的底层行为完全相同。方式三通过 enabledEvasions 精细控制如果你只想在 stealth 全家桶中保留少数几个 evasion可以在初始化时传入enabledEvasions或通过实例属性动态增删const StealthPlugin require(puppeteer-extra-plugin-stealth) // 初始化时指定启用集合 const plugin StealthPlugin({ enabledEvasions: new Set([ navigator.hardwareConcurrency, navigator.webdriver ]) }) puppeteer.use(plugin) // 或运行时动态删除 plugin.enabledEvasions.delete(navigator.webdriver)四、原理深剖源码级解析该 evasion 的核心逻辑在 index.js 的onPageCreated钩子中async onPageCreated(page) { await withUtils(page).evaluateOnNewDocument( (utils, { opts }) { utils.replaceGetterWithProxy( Object.getPrototypeOf(navigator), hardwareConcurrency, utils.makeHandler().getterValue(opts.hardwareConcurrency) ) }, { opts: this.opts } ) }拆解来看这条调用链包含几个关键设计1. evaluateOnNewDocument在页面脚本执行前注入withUtils见 withUtils.js是对page.evaluateOnNewDocument的封装。它的作用是在任何页面脚本运行之前把工具函数库_utils与主逻辑以字符串形式序列化注入到文档上下文保证目标网站的反检测脚本先看到的是被伪装后的属性。这是所有 stealth evasion 的通用注入时机。2. 为什么改的是原型而不是实例代码中修改的目标是Object.getPrototypeOf(navigator)即Navigator.prototype而非navigator实例本身。_utils 中的注释说明了原因You also want to work on the prototype of an object most often, as youd otherwise leave traces (e.g. showing up in Object.getOwnPropertyNames(obj)).如果直接给实例添加一个自有属性Object.getOwnPropertyNames(navigator)或属性描述符枚举就能立刻发现多出来的键反而留下痕迹而原生hardwareConcurrency本来就定义在原型上修改原型 getter 可以让一切看起来与原生浏览器一致。3. replaceGetterWithProxy用 Proxy 替换 getterutils.replaceGetterWithProxy见 _utils/index.js做了三件事utils.replaceGetterWithProxy (obj, propName, handler) { const fn Object.getOwnPropertyDescriptor(obj, propName).get const fnStr fn.toString() // 保存原生 getter 的字符串形式 const proxyObj new Proxy(fn, utils.stripProxyFromErrors(handler)) utils.replaceProperty(obj, propName, { get: proxyObj }) utils.patchToString(proxyObj, fnStr) // 让 toString 仍然返回原生样式 return true }取出原生 getter 函数用 JSProxy包裹它Proxy 的陷阱trap中返回我们指定的值通过Object.defineProperty把原型上的 getter 替换为这个 Proxy调用patchToString使Object.getOwnPropertyDescriptor(Navigator.prototype, hardwareConcurrency).get.toString()依然返回function get hardwareConcurrency() { [native code] }避免toString泄露 Proxy 痕迹。4. getterValue 陷阱先触发原生调用再返回假值utils.makeHandler().getterValue(value)见 _utils/index.js返回一个apply陷阱getterValue: value ({ apply(target, ctx, args) { // 先调用一次原生 getter触发并放大潜在错误 // 例如 navigator.__proto__.hardwareConcurrency 这类非法调用会在此抛出 utils.cache.Reflect.apply(...arguments) return value } })设计意图是hardwareConcurrency在原生规范中必须在合法 receiver 上调用如果检测脚本尝试用非法方式如从原型上剥离调用探测原生 getter 会抛出Illegal invocation类错误而这里先执行一次真实调用让错误在正确时机出现再返回我们伪造的值——既保留了原生的报错行为又覆盖了返回值。5. 痕迹清理stripProxyFromErrors 与 preloadCachestripProxyFromErrors_utils/index.js给 Proxy 的每个陷阱包一层 try/catch在陷阱抛错时从错误堆栈中剔除at Reflect.get、at Object.get等会暴露 Proxy 存在的行让错误堆栈看起来与原生一致。preloadCache_utils/index.js在注入时提前缓存Reflect.get、Reflect.apply的绑定副本以及原生Function.toString字符串。这样即使页面把Reflect.get等方法覆盖成嗅探器我们的 Proxy 内部使用的仍是缓存副本无法被监控到——这是针对高级检测手段的防御。五、测试验证伪装是否无痕仓库为该 evasion 提供了完整的 AVA 单元测试index.test.js可以直接运行验证cd packages/puppeteer-extra-plugin-stealth npm install npm run test:js四个测试用例分别验证了测试用例验证内容vanilla: matches real core count未启用插件时navigator.hardwareConcurrency等于os.cpus().length真实核心数证明测试环境的基线正确stealth: default is set to 4启用插件后页面里取到的值恒为默认值4stealth: will override value correctly传入{ hardwareConcurrency: 8 }时页面取到的值为8覆盖生效stealth: does patch getters properly伪装后Object.seal(getter)会抛错原生 getter 不可配置且get.toString()返回function get hardwareConcurrency() { [native code] }与真实浏览器完全一致其中第四个用例是无痕性的关键证明如果替换手段不够隐蔽getter 的toString()会暴露非原生实现而Object.seal抛错则说明属性描述符保持了原生 getter 的不可配置特性。此外在 service-worker.test.js 的指纹一致性检查中hardwareConcurrency与userAgent、language、deviceMemory等属性一起被纳入页面与 Service Worker 之间的指纹一致性对比用于防止页面伪装了、worker 没伪装导致的不一致漏洞。六、实战建议与注意事项取值建议贴合真实环境默认值 4 是插件作者参考公开统计分布后选定的但如果你控制的运行环境例如固定规格的服务器/容器真实核心数明确从源码看该参数接受任意 number建议结合实际部署环境设置为一个合理但不特殊的值例如与同机群其他真实浏览器行为接近的取值。请勿把该值设置成远超常见范围的极端数字否则反而可能成为新的指纹异常点。与整体 stealth 组合使用单个 evasion 只能解决一个维度。仓库的 fpscanner.test.js 表明未启用 stealth 的裸 puppeteer 会失败 78 项指纹检测项而启用全部 evasion 后特定 Chromium 版本下仅剩与webdriver相关的已知检测项。因此建议默认使用StealthPlugin()全家桶仅在确需精简时通过enabledEvasions裁剪。适用前提该 evasion 基于evaluateOnNewDocument注入与 Chromium 系原生的Navigator.prototypegetter 结构适用于 Chromium 内核的 puppeteer/playwright_utils的错误堆栈清理逻辑在注释中也明确目前仅支持 Chromium 系浏览器。猫鼠游戏的边界stealth 主插件文档明确承认完全阻止无头浏览器被识别几乎不可能目标只是把检测成本提高到不划算的程度见 stealth readme。hardwareConcurrency伪装同理——它降低的是一个维度的暴露面而非万能的免检证。七、相关源码索引evasion 插件本体类定义、默认参数与注入逻辑evasion API 文档本文依据的原始文档单元测试默认值、覆盖值、getter 无痕性验证共享工具库 _utilsreplaceGetterWithProxy、makeHandler、stripProxyFromErrors、preloadCache实现withUtils 注入封装evaluateOnNewDocument的序列化注入机制stealth 主插件evasion 集合与enabledEvasions配置插件基类defaults与opts的深合并机制Service Worker 一致性测试多执行上下文指纹一致性检查赞分享网页爬虫浏览器控制测试【免费下载链接】puppeteer-extra Teach puppeteer new tricks through plugins.项目地址https://gitcode.com/gh_mirrors/pu/puppeteer-extra点击查看免费下载相关推荐puppeteer-extra浏览器指纹伪装技术webgl.vendor与navigator插件应用puppeteer extra浏览器指纹伪装技术webgl.vendor与navigator插件应用 引言浏览器指纹追踪的隐形威胁 你是否曾疑惑为何刚浏览某网页爬虫浏览器控制测试硬件信息伪装实战指南内核级反检测工具深度解析硬件信息伪装实战指南内核级反检测工具深度解析 在当今数字化环境中硬件信息保护与系统伪装技术已成为开发者和安全研究人员关注的焦点。基于内核模式的硬件信息欺骗工应用安全驱动开发逆向工程终极指南如何使用puppeteer-extra插件实现网页自动化反检测终极指南如何使用puppeteer extra插件实现网页自动化反检测 想要在网页自动化中绕过反爬虫检测puppeteer extra插件是您的最佳选择?网页爬虫浏览器控制测试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考