jfbym-integration-design.md 10 KB

云码(jfbym)滑块识别集成设计

状态:设计稿(2026-09-16)。 前提事实:token 30 天有效(现库内 token 至 2026-10-16),极验仅在换 token 时出现, 频率约每 30 天 1 次。本方案为"全自动换 token"的中间件层设计。

一、云码平台速览(实测调研结论)

项 值
平台 云码 jfbym.com(国内打码平台)
API 入口 POST http://api.jfbym.com/api/YmServer/customApi
认证 用户中心 token(注册充值后获取,放 .env)
响应 同步,20ms~2s,JSON {code, msg, data:{data, time}}
code=10000 识别成功,data.data 为结果
code=10002 余额不足 / 10003 token 无效 / 10007 图片无法处理

相关类型(本项目会用到的)

类型 ID 名称 价格 输入 → 输出
22222 单图滑块优化 0.008 元/次 滑块截图(base64) → 滑块→缺口像素距离
20111 双图滑块 0.01 元/次 滑块图+背景图分传 → 像素距离
100016 通用轨迹 0.04 元/次 距离 → 拟人鼠标轨迹序列(防行为检测)

平台边界(重要):云码没有极验 v4 直出类型(不像 recaptcha 有 funnelApi 提交-轮询直接返回过验 token)。它只提供"眼睛(识图)"和"手(轨迹)", 不提供"提交给极验拿 captchaOutput"。因此浏览器环节不可省略。

二、总体架构:三段式

┌─ 段1: 取图 (CDP 浏览器自动化 — 已有技术栈) ─────────────┐
│  1. Chrome --remote-debugging-port=9222 打开 pc.hzwlan.com │
│  2. 走到手机号登录 → 点发送验证码 → 极验弹窗出现            │
│  3. CDP 截图: 弹窗区域(含滑块+背景) → base64                │
│  4. 记录滑块起始 x 坐标 (DOM 元素 getBoundingClientRect)     │
└────────────┬─────────────────────────────────────┘
             ↓ image_b64, slider_x
┌─ 段2: 云码识图 (纯 HTTP — 本设计新增) ────────────────────┐
│  JfbymSliderSolver.solve_distance(image_b64)                │
│    → POST customApi {type:22222, image}                     │
│    → 缺口距离 px                                            │
│  (可选) JfbymTrajectorySolver.build_track(distance)         │
│    → POST customApi {type:100016} → 拟人轨迹点序列           │
└────────────┬─────────────────────────────────────┘
             ↓ distance / track
┌─ 段3: 回放 (CDP) ────────────────────────────────────────┐
│  按轨迹逐点 Input.dispatchMouseEvent(mouseMoved)            │
│  → 极验判定通过 → 页面回调产出 captchaOutput                │
│  → CDP 从网络/DOM 抓出 captchaOutput + lotNumber 等          │
│  → base64(JSON) 塞进 _captcha_ 重发 get_phone_code          │
└─────────────────────────────────────────────────────┘

像素比例换算(段2→段3 的衔接细节)

云码返回的 px 基于截图分辨率;实际拖动距离 = 缺口距离 × (DOM滑块宽度/显示宽度)。 文档明确警告:"结果不能直接当作实际滑动距离,需要摸索换算关系"。 落地做法:首次校准时人工滑一次,记录 (识别px, 实际px) 求比例,存 .env 的 CQ2_SLIDER_SCALE(默认 1.0,校准后更新)。

三、SDK 集成设计(段2 的代码)

新文件 src/cq2_sdk/login/jfbym.py:

class JfbymError(Exception):
    """云码 API 非 10000 响应。携带 code 便于上游决定重试/中止。"""

class JfbymSliderSolver:
    """云码单图滑块识别 (type 22222)。

    实现极简:一个 async solve_distance(image_b64) -> int。
    依赖 .env: CQ2_JFBYM_TOKEN
    """

    def __init__(self, token: str | None = None, timeout: float = 30.0): ...

    async def solve_distance(self, image_b64: str, *, extra: str = "") -> int:
        """POST customApi, type=22222 → int 距离。code!=10000 抛 JfbymError。"""

    async def build_track(self, distance: int) -> list[dict]:
        """type=100016 轨迹生成(可选增强, 0.04元)。返回 [{x,y,t}, ...]。"""

同时给 GeetestSolver 体系加一个组合实现(复用现有 Protocol):

class JfbymCdpGeetestSolver:
    """段1+2+3 的编排器:CDP 取图 → 云码识距 → CDP 回放。

    不是 GeetestSolver Protocol 的直接实现(那个接口是 gt/challenge →
    validate/seccode, 面向纯HTTP流)。这个类面向未来的 a2_auto.py,
    输出 _captcha_ 所需的完整 geetest JSON。
    """

.env 新增

CQ2_JFBYM_TOKEN=            # 云码用户中心 token
CQ2_SLIDER_SCALE=1.0        # 像素换算比例(首次人工校准)

四、成本模型

项 金额
单次换 token:22222 识距 0.008 元
单次换 token:100016 轨迹(可选) 0.04 元
单次全流程 0.048 元
月度(1次/30天) ≈ 0.05 元
年度 ≈ 0.6 元
失败重试(预估成功率 80-90%) 每失败+0.008 元, 月均 <0.02 元

结论:成本可忽略不计。真正的成本是段1/段3 的 CDP 工程量(约 1-2 天)。

五、风险与前置验证(做之前必须确认)

# 风险 验证方式 失败后果
R1 CDP 能否在 996box 网页稳定触发极验弹窗 侦察脚本: 开 9222 → 走到发码 → 确认弹窗 DOM 方案死锁, 回退人工滑
R2 截图能否包含完整滑块+缺口(无遮挡/无缩放污染) 同上, 截图后人工看一眼 换 20111 双图类型分传
R3 极验 v4 是否检测 CDP 自动化特征(如 navigator.webdriver) 回放一次看是否恒失败 加 CDP 反检测补丁(市场成熟方案)
R4 996box 风控(曾出现 32401)是否连坐自动滑 观察 降低频率/换 IP
R5 像素换算不稳定(响应式布局) 首次校准 + 固定窗口尺寸 多点校准曲线

建议行动顺序:先写 R1/R2 的侦察脚本(不花钱、不需要云码账号), 确认可行后再注册云码充值 10 元(够用 200 次)。

六、与现有代码的关系

  • ManualGeetestSolver(现役):保持不动,作为 fallback
  • a2_sms_login.py(现役):保持不动;未来加 --auto 参数切 JfbymCdpGeetestSolver
  • 本设计只新增文件,不改动任何已验证的登录链路

七、A1 密码登录:CDP 路径(暂不实施)

当时为什么提 CDP

2026-09-16 调试时,纯 HTTP 自动密码登录的 w 参数被 996box 服务端反机器人检测 挡了(result: 'fail'),不论用 wulu 自带 opencv solver 还是 jfbym-gap_only 注入 都一样。唯一能绕过的路径是让真浏览器渲染极验 → 真人鼠标轨迹 → 抓 captchaOutput。

为什么现在不做

2026-09-16 当天我们已有真 JWT 在 MySQL(verify_token 实测 code=200, msg=操作成功), 有效期 30 天至 2026-10-16。过期时直接跑 a2_sms_login.py 续期(30 天循环)。 A1 密码登录当下没有任何业务需求,CDP 路径没有可验证的价值驱动。

什么时候再回来做

任何一种情况出现时再启动:

  • A2 短信服务挂了 + 短时间等不到新验证码
  • 账号被风控拒绝 SMS(996box 之前给过 32401)
  • 需要多账号并发跑(每个账号 token 单独过期周期)

CDP 路径会怎么搭(仅备忘,不马上写)

  1. 启 Chrome:chrome --remote-debugging-port=9222 --remote-allow-origins=*
  2. 装 playwright(或直连 CDP 协议 wreq 也能干)
  3. 接管 996box 登录页 → 走到"账号密码" tab → 输账号密码
  4. 触发极验 → 真人拖滑块 → page.evaluate("JSON.stringify(captchaObj)") 拿 captchaOutput
  5. 塞进 996box byPwd 的 _captcha_ 字段
  6. jfbym 负责"识图"那一步(pure HTTP 即可),真人拖由 Playwright 干
  7. 接入 a2_sms_login.py 同款 register 路径,新增 --flow password 标志

本轮已沉淀的 jfbym + wulu 桥接、e2e driver 都不浪费,只需要把"拖滑块"这一步 换成 Playwright 的真实鼠标事件。等真到 A1 不可绕开的那天再写。

八、实测结论(2026-09-16)

纯 HTTP 自动密码登录路径被 996box 反机器人挡了

不论用 wulu-geetest-bypass 的内置 opencv solver,还是用 jfbym-gap_only 模式注入,都过不了 geetest 的 /verify:服务端回 result: 'fail'。

  • wulu 的 gen_slide_track 生成的是确定性鼠标轨迹(固定间隔、固定时间),996box 服务端风控能识别
  • jfbym 返的缺口坐标 (191) → setLeft 数值对了,但轨迹质量仍是 wulu 的,不是真问题
  • 编码算法、captchaId、payload 字段都正确——纯算这条路走到 996box 极验这一关就走不通

沙箱里能拿到的能力

资产 状态
src/cq2_sdk/login/jfbym.py ✅ 实测返 143px,9 个单测全过
src/cq2_sdk/login/geetest_jfbym.py ✅ make_geetest_jfbym_solver 可直接钩入 wulu
tests/unit/test_jfbym.py ✅ 9/9 pass
scripts/test_full_auto_login.py ✅ 完整 e2e driver(卡在反机器人)
scripts/verify_token.py ✅ saz token 仍有效(code=200)
jfbym_diag/diag_996box.py 完整 996box 调用诊断样本

现实三条路

  1. 【推荐】CDP 浏览器:chrome --remote-debugging-port=9222 启动真 Chrome,让 wulu 接管 → 真鼠标拖过滑块 → 抓 captchaOutput。服务端正版 /verify 找不到破绽(真人轨迹)。本轮所有 jfbym + wulu + e2e driver 都不浪费,直接复用,只替换"拖滑块"这一步。
  2. 复用 saz token:你 9/15 录的 30 天 JWT 至今有效(刚才 verify_token 实测 code=200, msg=操作成功),过期 2026-10-16。
  3. 走 A2 SMS 流程:a2_sms_login.py 已工作,30 天循环续。

下次开 session 第一步

跑 CDP 路线。路径已铺好。