Turnstile 验证绕过 — 处理 Cloudflare 机器人检测
Cloudflare 的 Turnstile 是目前网站防机器人的主流方案之一。很多免费托管平台、注册页面、续费页面都套了这个。这篇文章拿 eooce/Auto-Renew-Bothosting 这个开源项目开刀,把它怎么绕 Turnstile 这件事,从头到尾讲清楚。
Turnstile 是什么?先搞明白你在对抗什么
Turnstile 是 Cloudflare 2022 年推出的 CAPTCHA 替代方案。和老式的图片验证码(点消防栓、识别红绿灯)不同,Turnstile 大多数时候对用户"无感"——你看到的就是一个转几圈的小圆圈,然后自动通过。
它怎么判断你是不是机器人?靠的是一套"客户端信号":
- 浏览器指纹:UA、语言、屏幕分辨率、插件列表……
- 行为特征:鼠标轨迹、点击习惯、页面停留时间
- 网络特征:IP 信誉、ASN、是否走了代理或 VPN
- JavaScript 执行环境:有没有 WebDriver 痕迹、有没有被自动化框架劫持
正常浏览器 + 正常用户的信号组合,Turnstile 直接放行。你用 requests 发个 HTTP 请求,什么信号都没有,直接被拦。
项目架构:这个脚本干的是什么
Auto-Renew-Bothosting 是一个自动续期脚本,目标是 bot-hosting.net 这个免费 Discord Bot 托管平台。免费账户需要定期手动点续期按钮,否则到期自动下线。这个脚本就是用来替你自动做这件事的。
整体流程如下:
1 | |
工具链:SeleniumBase(UC 模式)+ Python。
核心绕过:uc_gui_click_captcha() 是关键
先看这段代码:
1 | |
注意 uc=True。这里的 uc 是 SeleniumBase 的 Undetected-Chromedriver 模式,缩写自 undetected_chromedriver。
普通 Selenium 启动的 Chrome 有明显的 WebDriver 特征,Cloudflare 一眼就能识别出来:navigator.webdriver === true、Chrome 的自动化扩展 ID、DevTools 端口开放……Turnstile 全都会检测到,然后给你上 Challenge。
UC 模式干了什么:
- 对 ChromeDriver 二进制做 patch,移除可被检测的特征字符串
- 启动时注入 JS,把
navigator.webdriver改成undefined - 伪装浏览器指纹,让 Chrome 看起来像正常用户手动打开的
这样 Turnstile 拿到的信号,就是一个"看起来正常"的浏览器环境。
Turnstile 检测循环:三次重试机制
1 | |
uc_gui_click_captcha() 是 SeleniumBase 封装的方法,底层用 PyAutoGUI 模拟鼠标点击——不是 Selenium 的 click(),而是操作系统级别的 GUI 点击。
为什么要用 GUI 点击?因为 Turnstile 的 iframe 是跨域的,Selenium 的 JavaScript click() 无法穿透跨域 iframe 去触发它。操作系统级别的鼠标事件不受这个限制,直接落到屏幕坐标上,Turnstile 收到的就是一个"真实"的点击。
time.sleep(8) 是给 Turnstile 的判断逻辑留时间。它不是即时的,需要几秒钟跑完客户端信号采集和服务端校验。
三次重试是经验值:网络抖动、页面加载延迟都可能导致第一次失败,多试几次成功率明显提升。
wait_for_turnstile_pass:怎么判断是否通过
1 | |
思路很朴实:轮询页面源码,只要不包含这些 Cloudflare 特征文字,就认为通过了。
这里有个值得注意的点:它检测的是"不包含"而不是"包含某个成功标志"。因为 Cloudflare Challenge 页面有固定的文本,正常页面不会有。这种方式比找成功标志更通用——不管目标网站长什么样,只要 CF 的拦截页消失了,就说明通过了。
Cookie 注入:绕过登录流程
1 | |
脚本不走正常登录——不输用户名密码,直接注入已有的 session_token Cookie。
好处:
- 避开了登录页可能存在的额外 Turnstile 验证
- 速度快,减少被检测的窗口期
- 账号密码不需要写进脚本,更安全
session_token 从环境变量读取,配合 GitHub Actions Secrets 使用。Token 过期时,脚本会自动通过 gh CLI 更新 Secret:
1 | |
这个设计挺聪明——自动化续期本身顺便维护了自己的凭据。
到期日期提取:正则硬核匹配
1 | |
写了四个 pattern,处理了日期格式不统一的问题(YYYY/MM/DD 和 MM/DD/YYYY 两种)。遇到 MM/DD/YYYY 还做了格式转换,统一成 YYYY/MM/DD 输出。
这种写法的问题是:一旦目标网站改了页面结构或日期格式,就失效了。但对于稳定运行的小工具来说,够用。
代理支持:应对 IP 封禁
1 | |
如果你的服务器 IP 被 Cloudflare 判定为高风险(数据中心 IP、共享 IP、有历史恶意记录),Turnstile 会直接上 Challenge 甚至直接拒绝。这时候挂个住宅代理或低风险代理,成功率会高很多。
局限性和注意事项
这套方案不是银弹:
- Headless 模式成功率低:
HEADLESS=true时,Cloudflare 更容易识别出无头浏览器。脚本默认headless=false,在有桌面环境(或 VNC)的机器上运行。 - IP 质量影响极大:GitHub Actions 的公共 runner IP 是常见的数据中心 IP,Cloudflare 对这类 IP 的验证门槛更高。
- 不适合高频调用:这个脚本是每天跑一次的续期工具,不是高并发爬虫。高频使用 UC 模式容易触发更严格的检测。
- 目标网站随时可能加固:Cloudflare 在持续更新检测逻辑,今天能过不代表下个月还能过。
小结
整个 Turnstile 绕过模块的核心就三个东西:
- SeleniumBase UC 模式:伪装成正常浏览器,移除 WebDriver 特征
uc_gui_click_captcha():操作系统级鼠标点击,穿透跨域 iframe- 轮询页面源码:用 CF 拦截页特征文字判断是否通过
思路不复杂,工程细节是难点。适合用来理解"自动化脚本为什么能过 Cloudflare"这个问题——当然,也适合你自己搭类似工具时参考。
至于用于商业爬虫或绕过有明确禁止条款的网站?那不在本文讨论范围。