From 8186b24b706f1df8dd7c5212b0ab8f46df11f64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E8=B1=AA?= Date: Wed, 16 Sep 2026 15:59:03 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E5=9B=BE=E7=89=87=E8=8E=B7=E5=8F=96=E7=BB=8F=E9=AA=8C?= =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=88=E5=AF=B9=E6=8E=A5=E6=96=B9=E8=A7=86?= =?UTF-8?q?=E8=A7=92=EF=BC=9Acaptcha/get=20=E5=8F=96=E5=9B=BE=E3=80=81?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E6=B3=95=E3=80=81=E8=B8=A9=E5=9D=91=E6=B8=85?= =?UTF-8?q?=E5=8D=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 1 + .../captcha-image-acquisition.md | 135 ++++++++++++++++++ docs/experiments.md | 2 +- src/README_3rd.md | 2 + 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 docs/browser-automation/captcha-image-acquisition.md diff --git a/docs/README.md b/docs/README.md index c5f13df..3872c95 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,6 +19,7 @@ - [captcha-screenshot-capture.md](browser-automation/captcha-screenshot-capture.md) — wasm/GPU 渲染图片的 fromSurface=False 截图采集法(已被网络层法取代) - [human-drag-path.md](browser-automation/human-drag-path.md) — 拟人拖动路径生成算法(非直线/非匀速/过冲瞄准三要素) - **[captcha-auto-solve-pipeline.md](browser-automation/captcha-auto-solve-pipeline.md) — ★ 最新:密码登录触发法、网络层样本采集、tip_y 真值校准、自动闭环与 5014 行为风控现状** +- **[captcha-image-acquisition.md](browser-automation/captcha-image-acquisition.md) — 对接方视角:如何获取验证码大图与 mark 小图(/captcha/get 取 URL、DevTools 手动采集、触发与限流、踩坑清单)** ## 快速开始 diff --git a/docs/browser-automation/captcha-image-acquisition.md b/docs/browser-automation/captcha-image-acquisition.md new file mode 100644 index 0000000..29e4a4d --- /dev/null +++ b/docs/browser-automation/captcha-image-acquisition.md @@ -0,0 +1,135 @@ +# 经验:验证码图片的获取方法(大图 + mark 小图) + +> 面向对接方:要把图片喂给 `captcha_solver`,第一步是怎么把两张图拿到手。 +> 一句话结论:**不要从页面 DOM 拿图,从 `/captcha/get` 网络响应里取 URL 再下载**。 + +## 0. 背景:为什么不能从页面元素拿图 + +抖音系验证码(rmc-nocaptcha)的图片由 SDK 的 **wasm 解码后直接进 GPU 合成层**渲染: + +| DOM 手段 | 结果 | +| --- | --- | +| `img.src` / `currentSrc` | 是 iframe 页面自身 URL(占位),fetch 回来是 HTML | +| `img.naturalWidth` | 0(图片从未进入 img 解码管线) | +| `canvas.toDataURL()` | 输出全黑透明(RGBA 全 0) | +| CSS background / shadow DOM | 全部 none | + +但图片 URL 会**正常发 HTTP 请求**,响应 JSON 里就有完整的 CDN 直链 —— 这才是可靠入口。 + +## 1. 推荐方法:从 `/captcha/get` 响应取图(自动化/生产) + +### 1.1 响应结构 + +POST `https://verify.zijieapi.com/captcha/get` 的响应 JSON: + +```json +{ + "data": { + "question": { + "url1": "https://p9-catpcha.byteimg.com/...~tplv-188rlo5p4y-2.jpeg", + "url2": "https://p9-catpcha.byteimg.com/...~tplv-188rlo5p4y-1.png", + "tip_y": 58 + } + } +} +``` + +- `url1`:背景大图,jpeg,**552×344** +- `url2`:mark 小图,png **110×110**,带透明通道(RGBA) +- `tip_y`:缺口顶 y 提示(340 显示系),求解时**直接传原始值**,求解器内部已处理,别自己乘 2 + +### 1.2 下载与使用 + +图片托管在 `p3/p9-catpcha.byteimg.com`,**公开直链,无需登录态或签名**,GET 即得原始字节: + +```python +import base64, time, requests +from captcha_solver import solve_slide + +q = resp_json["data"]["question"] +big = requests.get(q["url1"], timeout=10).content # jpeg 原始字节 +mark = requests.get(q["url2"], timeout=10).content # png 原始字节(保持 PNG!) + +# 方式 A:直接喂求解器 +r = solve_slide(base64.b64encode(big).decode(), + base64.b64encode(mark).decode(), + tip_y=q["tip_y"]) + +# 方式 B:落盘成对保存(前缀一致,方便核对) +prefix = f"captcha_{int(time.time())}" +open(prefix + ".jpeg", "wb").write(big) +open(prefix + "-mark.png", "wb").write(mark) +``` + +下载到的字节**天然就是 552×344 / 110×110 规格**,与离线样本完全一致,无需任何缩放/转码。 + +### 1.3 三种拿到该响应的姿势 + +| 姿势 | 做法 | 适用 | +| --- | --- | --- | +| **CDP 监听(现成工具)** | `Target.setAutoAttach` + `Network.enable` 常驻监听,响应到达后 `Network.getResponseBody`,mime 为 image/* 或 URL 含 captcha/verify 的落盘 | 浏览器自动化。直接用 `try/captcha_net_capture.py`(输出 `try/captures/net_*_get.json` + 图片字节) | +| **中间代理** | mitmproxy / Charles / Fiddler 抓 `captcha/get` 导出 JSON | 有代理环境、不想写监听代码 | +| **自建协议请求** | 自己走完整 SDK 协议调 get 接口 | 仅适合已逆向 SDK 协议的团队;对接方不建议(需 captchaBody 加密轨迹) | + +## 2. 无代码快速验证:DevTools 手动采集 + +1. Chrome 打开目标站 → F12 → **Network** → 过滤框输入 `captcha` +2. 触发验证码(见 §3)→ 列表出现 `get` 请求 → 点开看 **Response** +3. 复制 `question.url1` / `url2` → 新标签页打开 → 右键另存为(或把 URL 直接发给求解方) +4. 自检尺寸(见 §6) + +## 3. 如何触发验证码(采集的前置条件) + +已验证最可靠的触发法 —— **密码登录法**: + +> 主页点「登录」→ 弹窗点「密码登录」→ 输手机号 + **随机错误密码**(如 `26969877Ab!`)→ 勾协议 → 点「登录」→ 必弹拖动重叠验证码。 + +密码错误提示是预期的,验证码照样触发。 + +工程要点: + +- **弹不出题(触发限流)→ 清 cookie 立即解除**:CDP `Network.clearBrowserCookies` 后用 `Page.navigate` 显式回站(清 cookie 后 session 失效,`reload` 会带到空页)。 +- **判断验证码是否真的弹出**:`iframe[src*="captcha"]` 的 `offsetParent !== null`(页面加载时就预加载了隐藏 iframe,`display:none` 不算触发)。 + +## 4. 兜底:抓不到网络时的截图法(不推荐,有损) + +仅当完全拿不到网络响应时应急。CDP `Page.captureScreenshot` **必须 `fromSurface=False`**——wasm/GPU 层不走默认 surface 截图通道,默认参数截出来是空白。 + +已知代价: + +- `clip` 参数被忽略,返回全屏需自行裁剪; +- mark 裁出来是**灰底、无 alpha 通道**,需按背景色反推 alpha 才能喂求解器; +- UI 显示是 340×212(缩放系数 ≈0.616),尺寸与求解器输入规格不符,属于有损数据。 + +细节见 [captcha-screenshot-capture.md](captcha-screenshot-capture.md)。 + +## 5. 高频踩坑清单(每条都实测踩过) + +| # | 坑 | 正确做法 | +| --- | --- | --- | +| 1 | 从 DOM / img.src / canvas 提图 | wasm 渲染层拿不到,走网络响应 | +| 2 | mark 转 JPEG 或从截图裁出(丢 alpha) | 保持 PNG 原始字节,求解器要求 alpha 通道 | +| 3 | 用 UI 显示尺寸(340 宽)的图去求解 | 用 CDN 原始字节,天然 552×344 / 110×110,无需缩放 | +| 4 | 手动重放/拼改 `captcha/get` 请求参数 | 会 `501 参数错误`;取响应里的 URL 直接 GET 即可 | +| 5 | 拿到题后慢慢处理 | challenge 有效期约 **60~90s**,超时 `NotFoundChallengeId`;预热/浏览必须放在**触发之前** | +| 6 | `tip_y` 自己乘 2 | 求解器内部已处理,直接传服务端原始值 | +| 7 | 存图命名随意 | 成对同前缀:`xxx.jpeg` + `xxx-mark.png` | +| 8 | 失败后高频重试 | 位置错换题即可;连续失败触发行风控 5014 需分钟级冷却或换环境(清 cookie) | + +## 6. 采集后 30 秒自检 + +```python +import cv2 + +big = cv2.imread("xxx.jpeg") +mark = cv2.imread("xxx-mark.png", cv2.IMREAD_UNCHANGED) +assert big.shape[:2] == (344, 552), f"大图尺寸异常 {big.shape}" +assert mark.shape[:2] == (110, 110) and mark.shape[2] == 4, f"mark 缺 alpha 通道 {mark.shape}" +print("样本规格 OK") +``` + +通过后即可联调: + +```bash +python captcha_solver.py xxx.jpeg xxx-mark.png --tip-y +``` diff --git a/docs/experiments.md b/docs/experiments.md index 09a2eb2..82d11bf 100644 --- a/docs/experiments.md +++ b/docs/experiments.md @@ -421,7 +421,7 @@ toast「验证成功,正在为你登录」。 完整特征诊断确认该题与 live_162840 同型,为当前特征体系的已知盲区: | 证据 | 数值 | 含义 | -|---|---|---| +| --- | --- | --- | | GT(175,~140) coarse 分(全尺度) | 6.82-7.73 平坦无谷 | mark 轮廓与低对比贴片不对齐,chamfer 无形状信号 | | 纹理假峰 B(366,128) coarse | U 型谷底 5.77 @ s=0.925 | 雪坡纹理被误判为轮廓贴合,霸占 NMS 前排 | | GT 进 top-24 候选池 | 否 | 候选池在粗扫阶段就漏掉真解 | diff --git a/src/README_3rd.md b/src/README_3rd.md index 94d863c..d3ad481 100644 --- a/src/README_3rd.md +++ b/src/README_3rd.md @@ -6,6 +6,8 @@ 典型场景:抖音系「拖动滑块完成拼图」验证码(`/captcha/get` 返回 `question.url1`(大图 552×344)与 `question.url2`(mark 110×110 PNG 含透明通道))。 +> **图片如何获取?** 两张图不从页面 DOM 提取(wasm 渲染层不可见),而是从 `/captcha/get` 响应取 `url1`/`url2` 直链下载——公开直链、无需签名,字节天然就是 552×344 / 110×110 规格。详细方法(DevTools 手动采集、触发法、限流处理、踩坑清单、采集后自检)见 [docs/browser-automation/captcha-image-acquisition.md](../docs/browser-automation/captcha-image-acquisition.md)。 + ## 2. 环境要求 - Python ≥ 3.10