Files
douyin-captcha/docs/browser-automation/captcha-auto-solve-pipeline.md
T
杨豪 cae37465d2 feat: 尺度精修上线,首次机器 PASS(live_160548 s=1.0375)
- method_l_shape: OK 候选尺度 ±0.0375 精修(步长 0.0125 + ±6px 窗口),
  GT live_165204 Δ 179→176(真人 177),离线 9/10 保持
- 插桩 JSON.stringify 捕获 SDK 日志明文;指针 probe 排除 coalesced/pressure
- 拖动不跟手指标作废:post_pos 晚于 SDK 重置滑块,鼠标事件流始终完整
- 剩余 VerifyErr = 候选整体选错(视觉确证),方向:多候选质量评估
- docs: experiments/solution/retrospective 同步收尾
2026-09-15 16:20:05 +08:00

5.6 KiB
Raw Blame History

经验:验证码自动通过闭环(采集→求解→拖动)与行为风控现状

更新:2025-09-11 晚。此前 captcha-screenshot-capture.mddouyin-captcha-trigger-attempts.md 的方法 已被本篇取代/升级,本篇是当前最新状态。

1. 可复现触发法(已验证,最可靠)

密码登录法:主页点「登录」→ 弹窗点「密码登录」→ 手机号 18601010101 + 随机密码(如 26969877Ab!)→ 勾协议 → 点「登录」→ 拖动重叠验证码必弹。 (密码错误提示是预期的,验证码照样触发;真人手动拖可通过)

2. 网络层样本采集(取代 DOM/截图法,try/captcha_net_capture.py

验证码图片不走 <img src>wasm 解码进 GPU 层),但图片 URL 会正常发 HTTP 请求

  • verify.zijieapi.com/captcha/getPOST)→ 响应 JSON 含 question.url1(大图 552×344 jpeg)、url2mark 110×110 RGBA png)、tip_y
  • 图片托管在 p3/p9-catpcha.byteimg.com,直接是原始字节

websocket 常驻监听(Target.setAutoAttach + Network 域 + getResponseBody)即可 把原图字节落盘 —— 与 captchas/ 离线样本完全同规格,无需缩放solve 直接可用。

3. tip_y 服务端提示(重大发现)

tip_y × 2 = 缺口 y 坐标(mark 画布顶,原图 552×344 坐标系)。 10 组线上样本校准:solver 无约束 y 与 2×tip_y 差 ≤4px 的样本全部位置判定正确; 偏差大的(如 Δy=17)正是被服务端判 VerifyErr 的。

try/live_solve.py:用 tip_y 约束 y,在 score map 上重选 x —— 服务端等于 免费下发了 y 真值,只需求 x。

4. 拖动闭环(try/auto_solve_drag.py

CDP websocket 直连全流程(不依赖 browser-use CLI):点刷新 → 轮询 get.json 落盘 → live_solve → human_drag.gen_human_path 拟人路径 → Input.dispatchMouseEvent 拖滑块 → 读 verify 结果。get→verify 全程 <8schallenge 有效期约 60-90s 超时会 NotFoundChallengeId)。

实测确认:

  • 滑块拖动 1:1 映射 mark 位移(拖 100px → mark 移 100px
  • 拖动距离 = solver x × 340/552UI 缩放)
  • 服务端响应三种:验证通过 / VerifyErr(轨迹过、位置差)/ 5014 操作过快(轨迹被拒)

5. 剩余 gap:5014 行为判定(未解决)

VerifyErr 与 5014 交替出现 = 位置与轨迹都接近临界。真人可过而脚本被 5014 拒, 原因在 captchaBody(~6.3KB 加密轨迹密文,含 w2/x86 设备指纹):

  • 轨迹明文由 SDK wasm 加密,离线解不开
  • 我的拟人路径(贝塞尔+噪声+过冲回拉)仍被判机器 —— SDK 采集的不止鼠标点, 可能含 pointermove 原生事件流、movementX、事件时间戳分布、设备指纹等
  • 下一阶段方向:hook SDK 内部轨迹采集函数(wasm 前的 JS 层),直接注入 自定义轨迹数组,让 SDK 自己加密上报;或对比真人 captchaBody 字节结构定位 采集字段

6. 文件清单

文件 用途
try/captcha_net_capture.py 网络层常驻监听,抓 get/verify 响应 + 图片字节
try/captcha_verify_watch.py 专抓 verify 请求 POST 全文(captchaBody 对比用)
try/live_solve.py tip_y 约束版求解(线上样本专用)
try/human_drag.py 拟人拖动路径生成器
try/auto_solve_drag.py 一条龙闭环(刷新→solve→拖→读结果)
try/captures/pair_* 已采集线上样本对(17 组)

7. 后续实验结论(2026-05-15/16xdotool 硬件输入 + 换号循环)

上述"下一阶段方向"已被系统性实验否定——输入层不是根因

  • xdotoolOS 级 XTest 硬件鼠标) 拖 solve 位置 → 仍 VerifyErr 事件属性(pressure/movementX/coalesced/isTrusted)与真人无差异;
  • 真人轨迹重放human_trace_pass.json,含 20-266ms 抖动间隔、4px y 振幅、 0.7s 终点停顿)→ 仍 VerifyErr
  • 会话预热(触发前 45-75s 页面游走)→ 不改变结果;
  • 真人 PASS 与机器拖的差异只剩「行为分窗口」(5014 = 窗口内累计失败, 真人 PASS 夹在机器失败之间仍能过)与「位置小偏差」(±10px 级,视觉确证 live_191109 solve 正确但 live_135654 贴片在 solve 块内略偏)。

触发与限流工程结论

  • 触发限流(不出题):清 cookie 即解除(Network.clearBrowserCookies + Page.navigate 回 douyin)——用户实测确认,无需等待;
  • 触发拒绝误报:错误检测正则勿包含"验证码登录"(tab 文字本身); 真正的号段拒绝文案是"仅支持验证码登录";
  • reload 坑:清 cookie 后 session 失效,Page.reload 会把页面带到 空标签/跳转页,必须用 Page.navigate 显式回 douyin
  • challenge 时效:约 1 分钟,采题后的任何预热/等待都会耗死题 NotFoundChallengeId)——预热必须放在触发之前
  • 拖动时长保真:重放轨迹时 up 前最后一步的 dt(真人 ≈0.7s 终点对齐) 不能丢,否则 2.6s 被压成 1.9s;跳过 0 位移步时 t_plan 仍要累加。

文件

文件 用途
try/rotate_loop.py 清 cookie + 换号零等待循环(风控即换号)
try/cold_start_attempt.py 单次完整尝试(预热/采题/选优/xdotool 拖动)
try/enrich_replay.py 重放轨迹 60Hz 加密(实验结论:勿用,真人事件流稀疏)
try/human_trace_pass.json 真人 PASS 轨迹(live_1652042.60s/17 move