Files
douyin-captcha/docs/browser-automation/captcha-screenshot-capture.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

3.3 KiB
Raw Blame History

经验:拖动验证码页面截图采集(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.getResponseBodyverify.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"]))

要点:

  1. fromSurface=False 走非合成层通道,能拿到真实像素(经实测验证: 同一区域 fromSurface=True 灰板、False 有图)。
  2. clip 参数会被忽略fromSurface=False 时返回全屏 1920×993),需 Python 裁剪。
  3. 裁剪坐标 = iframe 视口位置 + iframe 内元素位置:
    • iframe 在视口 (770, 305),大小 380×384
    • 大图在 iframe 内 (20, 69)340×212 → 视口裁剪 [374:586, 790:1130]
    • mark(未拖动时)与大图同起点,68×68 → 视口裁剪 [374:442, 790:858]
  4. 先在 iframe 内用 Runtime.evaluate 取元素 getBoundingClientRect() attach iframe target 后,坐标系是 iframe 内部坐标系,注意换算)。

验证码 iframe 布局(v2380×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×110RGBA)。 线上 UI:大图 340×212、mark 68×68,缩放系数 ≈ 0.616340/552)。 solve 的 distance_px 是原图坐标系,执行拖动时乘以缩放系数换算成 UI 像素。