invisible_playwright 架构深潜:wrapper、invisible_core 与 Juggler 如何协同实现反检测浏览器
【免费下载链接】invisible_playwrightPlaywright that anti-bots cannot see, so no captchas: same API, anti-detect stealth headless Firefox, undetected fingerprint, bypass bot detection, Python web scraping and browser automation.项目地址: https://gitcode.com/gh_mirrors/in/invisible_playwright
invisible_playwright 是一个用 Playwright 原生 API 驱动反检测 Firefox的 Python 库:同一份代码、同样的page.goto/page.click,底层却是一个在 C++ 源码级打补丁的无头/有头浏览器,配合确定性指纹,让 reCAPTCHA、Cloudflare 等反机器人系统"看不见"自动化痕迹。它的秘密不在某个魔法函数,而在三层架构的精密分工——wrapper 负责门面、invisible_core 负责身份、Juggler 负责驱动。本文带你逐层拆解它们如何协同工作。
一张表看懂三层架构 🧩
| 层 | 角色 | 关键模块 |
|---|---|---|
| wrapper(外壳层) | 用户唯一接触面:InvisiblePlaywright上下文管理器、版本守卫、人性化鼠标 | launcher.py、_session.py、_cursor.py |
| invisible_core(身份层) | 种子 → 指纹 → 浏览器偏好的"唯一事实来源",外加代理、时区、地理信息 | _fpforge/、seal 封印校验 |
| Juggler(驱动层) | 进程内 Python 协议服务器,经管道直连 Firefox 内置的 Juggler 端点,彻底告别 Node 驱动 | _juggler/(connection.py、server.py、dispatcher.py) |
一句话概括数据流:你写的 Playwright 调用 → wrapper 翻译并注入人性化动作 → invisible_core 提供的偏好/指纹随启动参数进入浏览器 → Juggler 管道把每条命令送达 Firefox 内部协议端点。
第一层:wrapper —— 两行代码切换的入口
对使用者而言,整个架构只有一个面孔:InvisiblePlaywright。切换只需两行:
- from playwright.sync_api import sync_playwright + from invisible_playwright import InvisiblePlaywright这层做了三件"看不见的重活":
- 版本守卫:导入瞬间 _pin.py 就校验已安装的 invisible-core 是否精确匹配声明版本,不匹配会尝试自动修复而不是静默出错;_engine.py 再校验 Playwright 客户端版本落在引擎"封印"(seal)声明的范围内——因为 Juggler 协议是闭集,超范围的客户端会在创建上下文时直接失败。
- 同步/异步合一:
launcher.py(sync)与async_api.py(async)两个类原本有 80% 重复代码,后来抽出了 _session.py 作为共享会话逻辑,保证两条 API 路径行为永远一致。 - 人性化注入:_cursor.py 不新增任何 API,而是拦截
page.click/page.hover等所有指针动作的内部汇聚点,把"瞬移光标"改写成由种子绘制的贝塞尔曲线轨迹——同样的seed可以精确重放同样的运动。
第二层:invisible_core —— 指纹的唯一事实来源 🧬
反检测浏览器被抓包的头号原因,是各指纹面互相矛盾(比如 User-Agent 说 Windows,字体却像 Linux)。invisible_core 的解法是:所有值由一个种子统一采样,在页面能提问之前就锁定。
with InvisiblePlaywright(seed=42) as browser: # 同一 seed = 同一 GPU、Canvas、音频指纹 ...- 种子 → Profile → 偏好:
generate_profile用贝叶斯采样器从种子派生约 200 个字段(GPU、屏幕、字体、音频、时区……),再由compose_session_prefs翻译成 Firefox 偏好项写入配置。想要固定某些字段,用pin=即可,详见 docs/pinning.md。 - 引擎封印:
ensure_binary下载打过补丁的 Firefox 二进制,seal 文件记录其构建指纹,_engine.py 在启动前后双重校验——连application.ini被手改都能被协议层上报的版本戳识破。 - 网络一致性:时区从出口 IP 离线解析、DNS 强制走代理、WebRTC 只提供一个承载代理出口的合成 ICE 候选——本地真实 IP 无处泄漏:
指纹站看到的效果:Bot、开发者工具、虚拟机等信号全部"Not detected":
第三层:Juggler —— 没有 Node 的驱动通道 🚀
传统 Playwright 是"Python 客户端 → Node 驱动 → CDP/Juggler"三段式,而 invisible_playwright 在 2026-08-28 删除了整个 6MB 的 Node 驱动:现在回答 Python 客户端的,是进程内的 Python 服务器,位于 _juggler/。
它的管道契约直接读自 Firefox 源码而非猜测:
- 消息是零字节分隔的 JSON(不是换行);
- POSIX 上文件描述符硬编码为fd 3 / fd 4;Windows 上用可继承的句柄,经环境变量
PW_PIPE_READ传递; - 命令行必须带
-juggler-pipe,否则 Windows 上管道根本不激活。
协议层有两个硬核设计(见 dispatcher.py 的模块注释):
- 顺序即协议:每个新对象必须先发
__create__宣告、再返回 channel 句柄,乱序不是"偶尔能跑"的竞态,而是硬失败; - 方法显式路由:每个协议对象用显式的
METHODS字典声明可响应的方法名,杜绝getattr动态路由带来的攻击面。
而 server.py 中的每个字段形状都来自scripts/capture_protocol.py对真实会话的协议录制——比如goto/click发给的是 Frame 而非 Page,mouseMove在一次人类化点击中会到达 19 次,因为光标真的在移动。
一次点击如何穿过三层 🔍
以page.click("#submit")为例,完整旅程是:
- wrapper 层:
_cursor.py拦截这次点击,按种子展开成一条带按压、带节奏的贝塞尔轨迹,拆成多次mouse.move加一次落点; - 驱动层:每条指令经 Juggler 管道(零字节分隔 JSON)送达,
dispatcher按 guid 路由给对应协议对象,事件顺序严格符合"先创建、后引用"; - 引擎层:Firefox 内部由真实输入路径派发事件,所以
event.isTrusted是"真的真",而非 JS 垫片伪造; - 身份层兜底:整个会话中,
navigator、Canvas、WebGL、WebRTC 全部回答来自同一个种子画像,页面无论怎么交叉验证都不会自相矛盾。
结果就是检测站视角下的"0% headless、0% stealth 特征":
延伸阅读 📚
- 完整入口与用法:README.md
- 指纹字段固定(pinning)详解:docs/pinning.md
- 指南总目录(含检测原理分组):docs/guides.md
- 测试如何验证各层行为:tests/(如 test_juggler_connection.py、test_fingerprint_consistency.py)
【免费下载链接】invisible_playwrightPlaywright that anti-bots cannot see, so no captchas: same API, anti-detect stealth headless Firefox, undetected fingerprint, bypass bot detection, Python web scraping and browser automation.项目地址: https://gitcode.com/gh_mirrors/in/invisible_playwright
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考