Go 绕反爬,我选了 go-juggler + Camoufox
有个 Agent 要登录后台系统。本地 Playwright 跑得好好的,换到服务器上就被 Cloudflare 挡下来,页面都加载不出来。Headless 指纹太明显,隐身插件加进去反而多了一组异常特征。
后来把方案换成 go-juggler + Camoufox。go-juggler 是 Juggler 协议的 Go 客户端。Juggler 是 Firefox 的自动化协议,比 CDP 更早出现,由 Puppeteer 项目维护,走 JSON-RPC 线格式。它和 CDP 解决的是同一类问题,差别在目标浏览器是 Firefox 而不是 Chromium,而且 Juggler 是独立模块,方便修补。选它主要就是为了配 Camoufox。
为什么是 Camoufox
Camoufox 在 C++ 层改指纹:WebGL 渲染器、AudioContext、屏幕几何、WebRTC、navigator.hardwareConcurrency。改动在 JS 执行之前完成,页面脚本读到的就是改过的值,查不到自动化痕迹。这是它能绕过的核心——不是在 JS 层面做掩盖,而是让整个执行环境本身就"不像"自动化。
REST 客户端优先,低层先别碰
go-juggler 分两层。高层 REST 客户端走 camofox-browser 的 HTTP API,日常够用;低层走 OS 管道(fd 3/fd 4)或 WebSocket,控制更细,但传输 API 还在开发中,不建议依赖。REST 客户端的基本用法:
docker run -d -p 9377:9377 --name camoufox yvv4docker/camoufox:latest
c := juggler.NewClient("http://localhost:9377")
// 打开标签页:注意第二个参数是会话名
tab, _ := c.OpenTab(ctx, "demo", "https://example.com")
defer c.CloseTab(ctx, tab.TabID, "demo")
// 快照:返回 ARIA 无障碍树,不是原始 HTML
snap, _ := c.Snapshot(ctx, tab.TabID, "demo")
最常用的是 Snapshot。它返回 ARIA 无障碍树,每个可交互元素带 [e1] 这种稳定引用,Agent 按引用点击,不用跟 CSS 选择器搏斗。体积比 HTML 小约 90%,调 LLM 时省 token。这一点对 AI Agent 场景很关键:页面越复杂,原始 HTML 的 token 成本越难看。
另外两个常用的是 Evaluate 和 NetworkRequests:
// 页面上下文执行 JS
title, _ := c.Evaluate(ctx, tab.TabID, "demo", `document.title`)
// 一次性抓页面加载的所有资源
reqs, _ := c.NetworkRequests(ctx, tab.TabID, "demo")
for _, req := range reqs {
log.Printf("[%s] %s", req.Type, req.URL)
}
踩过的坑
Evaluate 端点要求 camofox-browser >= 1.4.0。旧版本直接返回 404,客户端检测到就报错,升级镜像即可,现象很明确。
另外两点要提前接受:
- 项目整体是 MVP,API 覆盖有限(Links、Screenshot、Stats 这些都有,但别指望文档像 Playwright 那么全)。
- 低层传输 API 不稳定,想精细控制协议层,现在还不是时候。
自己跑采集、做 Agent 验证可以,别当生产级方案接进核心流程。
什么情况别用
- 只抓静态页面,用 net/http 就行,别为它起一个浏览器进程。
- 靠验证码或行为分析拦截的站,Camoufox 改指纹也绕不过。
- 已经在 Chromium 栈上投了不少代码,换 Firefox 系要算迁移成本,不是零代价替换。
如果只是给 LLM Agent 加一个"能读真实网页"的通道,go-juggler + Camoufox 是目前 Go 生态里少数能落地的组合,可以试。