状态:设计稿(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 │
└─────────────────────────────────────────────────────┘
云码返回的 px 基于截图分辨率;实际拖动距离 = 缺口距离 × (DOM滑块宽度/显示宽度)。
文档明确警告:"结果不能直接当作实际滑动距离,需要摸索换算关系"。
落地做法:首次校准时人工滑一次,记录 (识别px, 实际px) 求比例,存
.env 的 CQ2_SLIDER_SCALE(默认 1.0,校准后更新)。
新文件 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。
"""
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(现役):保持不动,作为 fallbacka2_sms_login.py(现役):保持不动;未来加 --auto 参数切 JfbymCdpGeetestSolver2026-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 路径没有可验证的价值驱动。
任何一种情况出现时再启动:
chrome --remote-debugging-port=9222 --remote-allow-origins=*playwright(或直连 CDP 协议 wreq 也能干)page.evaluate("JSON.stringify(captchaObj)") 拿 captchaOutputbyPwd 的 _captcha_ 字段a2_sms_login.py 同款 register 路径,新增 --flow password 标志本轮已沉淀的 jfbym + wulu 桥接、e2e driver 都不浪费,只需要把"拖滑块"这一步 换成 Playwright 的真实鼠标事件。等真到 A1 不可绕开的那天再写。
不论用 wulu-geetest-bypass 的内置 opencv solver,还是用 jfbym-gap_only 模式注入,都过不了 geetest 的 /verify:服务端回 result: 'fail'。
gen_slide_track 生成的是确定性鼠标轨迹(固定间隔、固定时间),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 调用诊断样本 |
chrome --remote-debugging-port=9222 启动真 Chrome,让 wulu 接管 → 真鼠标拖过滑块 → 抓 captchaOutput。服务端正版 /verify 找不到破绽(真人轨迹)。本轮所有 jfbym + wulu + e2e driver 都不浪费,直接复用,只替换"拖滑块"这一步。code=200, msg=操作成功),过期 2026-10-16。a2_sms_login.py 已工作,30 天循环续。跑 CDP 路线。路径已铺好。