- 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 同步收尾
3.3 KiB
3.3 KiB
经验:拖动验证码页面截图采集(CDP)
场景:验证码图片渲染在 wasm/GPU 层(
img元素naturalWidth=0、canvas 0×0、 src 是占位 URL),常规 DOM/canvas 提取拿不到真实位图;用户手动右键「另存为」可以。
为什么 DOM 方式全失败
抖音 rmc-captcha 的图片不走标准 <img src>:
| 尝试 | 结果 |
|---|---|
img.src / currentSrc |
是 iframe 页面自身 URL(占位),fetch 回 HTML |
img.naturalWidth |
0(图片数据从未进入 img 元素解码管线) |
canvas.toDataURL()(drawImage 复制) |
canvas 未污染但输出全黑透明(RGBA 全 0) |
CSS background-image(元素/伪元素/父链/shadow DOM) |
全部 none |
Network.getResponseBody 抓 verify.zijieapi.com/captcha/get |
响应体是图片字节但无法与 DOM 对齐;手动 GET 重放会 501 参数错误 |
Page.captureScreenshot(默认 fromSurface=true) |
iframe 区域空白——wasm/GPU 合成层不进该截图通道 |
结论:图片由 wasm 解码后直接进 GPU 合成层,DOM 与 JS 均不可见。
正解:fromSurface=False 全屏截图 + Python 裁剪
r = cdp("Page.captureScreenshot", format="png",
captureBeyondViewport=False, fromSurface=False) # 关键参数
open("/tmp/full.png", "wb").write(base64.b64decode(r["data"]))
要点:
fromSurface=False走非合成层通道,能拿到真实像素(经实测验证: 同一区域fromSurface=True灰板、False有图)。- clip 参数会被忽略(
fromSurface=False时返回全屏 1920×993),需 Python 裁剪。 - 裁剪坐标 = iframe 视口位置 + iframe 内元素位置:
- iframe 在视口
(770, 305),大小 380×384 - 大图在 iframe 内
(20, 69),340×212 → 视口裁剪[374:586, 790:1130] - mark(未拖动时)与大图同起点,68×68 → 视口裁剪
[374:442, 790:858]
- iframe 在视口
- 先在 iframe 内用
Runtime.evaluate取元素getBoundingClientRect()(attach iframe target 后,坐标系是 iframe 内部坐标系,注意换算)。
验证码 iframe 布局(v2,380×384)
iframe (770,305) 380x384
├─ close-btn (326,0) 54x54 右上角关闭
├─ 标题栏 .captcha-bar (20,12) 340x69 「请将左侧图形拖到正确位置」
├─ 大图 .verify-image (20,69) 340x212 大图区域
│ └─ mark(初始态) (20,69) 68x68 与大图同起点(未拖动时)
├─ 滑条 .captcha-slider(20,293) 340x44 底部滑轨+拖钮
└─ 刷新/反馈 (20,345) 页脚操作行
mark 图的坑(RGB 采集)
截图裁出的 mark 是带灰底的(页面背景 246,245,244),与离线样本 (PNG 透明底 + alpha 轮廓)不同。喂给 solve 前需转 RGBA:
mark = cv2.imread(mark_crop_path)
bg = np.array([246, 245, 244])
diff = np.abs(mark.astype(int) - bg).sum(axis=2)
alpha = np.clip(diff * 3, 0, 255).astype(np.uint8)
rgba = cv2.merge([mark[:, :, 0], mark[:, :, 1], mark[:, :, 2], alpha])
尺寸缩放(原图 vs UI 显示)
离线样本:大图 552×344、mark 110×110(RGBA)。
线上 UI:大图 340×212、mark 68×68,缩放系数 ≈ 0.616(340/552)。
solve 的 distance_px 是原图坐标系,执行拖动时乘以缩放系数换算成 UI 像素。