为什么你的爬虫在 5-6 个并发 Chrome 实例时出现瓶颈(以及没人提及的 shared-cookie 陷阱)
Source: Dev.to
为什么标签页会破坏 Turnstile(以及其他 CF 挑战)
Cloudflare 的 Turnstile 小部件有两点会让它在同一 Chrome 进程的多标签页抓取环境中表现得很敌对:
- 它会检查
document.visibilityState。 被置于后台的标签页会报告为hidden,小部件的挑战脚本会因此退出或停顿,等待一次visible转换。这正是原作者观察到的 “Turnstile 在非聚焦页面上加载不正常” 的原因。 - Cookie 在整个浏览器配置文件中是共享的。 当两个标签页在同一源上同时启动 CF 挑战时,它们会争抢同一个
cf_clearanceCookie 槽。先获得焦点的标签页会写入它的令牌;另一标签页的挑战则会看到状态不匹配而被阻止。
线程中的一条回复简洁地点出了关键:“浏览器在所有标签页之间共享 Cookie,多个挑战会相互阻塞。” 这就是完整的原因。焦点只是一种表现形式,真正导致冲突的是 Cookie 竞争。
为什么“最多 5‑6 个浏览器”是错误的数字
如果你对这 5‑6 个 Chrome 进程进行性能分析,你会看到:
- 每个约 200 MB RSS,大部分是来自 V8 和渲染器的堆内存
- 两三条渲染线程在等待页面加载 / 绘制时空转
- 一个用于网络和加密的工作线程池
在一台 16 GB / 8‑核 的机器上,你受到的是 CPU 限制,而不是内存限制,因为每次页面加载都会触发完整的 Chromium 渲染 + JS 执行 用于挑战脚本——这本身故意设计得很耗资源(这就是工作量证明中的“工作”部分)。
所以真正的上限不是“有多少 Chrome 可执行文件能装进内存”,而是“你的 CPU 能并行解决多少个 CF 挑战”。在作者的 5‑6 个浏览器下实现 30 次解答/分钟,相当于每个浏览器约 5 次/分钟,或大约每 10‑12 秒一次。这与冷启动时挑战所需的时间相符。
配置文件隔离修复
逃离并不是更多的线程或标签页,而是 带缓存清除的配置文件隔离。
思路
- 预热一组 Chrome 配置文件(例如 20 个),让每个配置文件完成一次 CF 挑战并保存生成的
cf_clearanceCookie。 - 对于每个爬取请求,挑选一个清除尚未过期的配置文件(CF 清除通常约 30 分钟有效)。
- 使用该配置文件进行爬取。由于清除已存在,不会再触发挑战——省去约 10 秒的工作量证明。
- 当配置文件的清除过期时,悄悄在后台重新预热它。
采用此架构后,瓶颈从“每个浏览器的 CF 挑战计算”转移到“每页的网络延迟”,从而可以并发数十个请求。
生产运行的实际数据
- 20 个预热的配置文件
- 在已预热清除的 CF 受保护目标上,平均页面加载约 1.5 秒
- 吞吐量约为“每页一个全新挑战的浏览器”方案的 8 倍
使用 browser-act 实现
browser-act 是一个 CLI 工具,帮助你管理配置文件池——每个 browser-id 都是一个拥有独立 cookie 和存储的隔离配置文件。
# 安装:
npx skills add browser-act/skills --skill browser-act
# 创建 20 个配置文件:
for i in $(seq 1 20); do
browser-act browser create --profile-name "scrape-$i"
done
# 通过打开目标站点一次(运行 Cloudflare 挑战)来预热每个配置文件:
browser-act --session warmup browser open https://target.site
# 稍后,在你的爬虫中,挑选一个已预热的配置文件并运行:
browser-act --session scrape browser open https://target.site/page-N
browser-act --session scrape get markdown > page-N.md
Cookie 会在每个配置文件的磁盘上持久化,因此重启爬虫也不会丢失已获取的授权。
值得争论的点
- 如果目标站点在每个请求时轮换挑战策略(一些银行/博彩站点会这样),这并没有帮助。这是另一种情况——你需要一个 JS 求解器循环。
- 大约 20 个配置文件是单台机器上收益递减的临界点。超过这个数量,应该放在不同的实例并使用不同的 IP——
cf_clearanceCookie 与 IP 绑定。 - 如果你只需要大约每分钟 30 页,原帖的 5‑6 浏览器设置就足够。超过每分钟约 100 页时,配置文件隔离的方法才重要。
Full r/webscraping thread: optimised chrome? for multi threading. 如果你也在面对同样的上限,直接把你尝试过的方案贴出来——我很乐意比较。