# 经验:拟人拖动路径生成算法(反检测三要素) > 场景:验证码行为风控会检测拖动轨迹的「机器特征」。人手拖动的轨迹与 > CDP `Input.dispatchMouseEvent` 生成的匀速直线完全不同。 > 代码:`try/human_drag.py`(`gen_human_path(distance_px)` → 增量事件序列)。 ## 三要素(用户需求 → 实现机制) ### 1. 不能是直线 → 贝塞尔弓形骨架 + 高斯抖动 ``` 主冲程路径 = 三次贝塞尔 P0(0,0) → P3(过冲点) P1/P2 控制点带 y 轴弓形偏移(±8~14px,随机方向) 每步叠加 = gauss 抖动(x: σ≈0.8, y: σ≈1.1) 抖动幅值随速度变化:中段速度快 → 手更不稳(σ 放大) ``` 弓形是关键:不是噪声围绕着直线,而是整体轨迹带自然弧度(人手腕转动)。 y 轴全程摆动 ±5px 左右,终点收敛回 0。 ### 2. 不能匀速 → 三段速度结构 ``` 主冲程 (55-70% 时长):ease-in-out(加速→巡航→减速),14-22ms/步 回拉 (15-22% 时长):ease-out 快速回退 微调 (剩余时长) :步长递减的小步逼近 + 末尾 40-120ms 停顿确认 ``` 速度统计上呈「快-慢-停」节奏,`v_max ≈ 0.5-0.95 px/ms`、`v_min ≈ 0.001` (慢两个数量级),与匀速(速度方差≈0)完全可区分。 ### 3. 不能一次到位 → 过冲-回拉-瞄准 ``` 阶段1 主冲程 → 目标 + 过冲量(3~9px 随机) 阶段2 回拉 → 目标 - 欠量(0.5~2.5px) 阶段3 微调 → 1~2 小步逼近精确目标,每步 15ms+ ``` 人类视觉反馈回路的标配:先冲过头,再修正。位移曲线 x-t 上 可见明显的「超过目标 → 拉回 → 精确落点」三段形态。 ## 用法 ```python from human_drag import gen_human_path, path_stats pts = gen_human_path(distance_px=227.9, seed=42) # seed 可复现 # pts = [(dx, dy, dt_ms), ...] 相对上一点增量 for dx, dy, dt in pts: cur_x += dx; cur_y += dy cdp("Input.dispatchMouseEvent", type="mouseMoved", x=cur_x, y=cur_y, button="left", buttons=1) time.sleep(dt / 1000) ``` 拖动距离换算:solve 的 `distance_px` 是原图(552 宽)坐标, 乘缩放系数 `UI宽/552`(如 340/552≈0.616)得 UI 像素。 ## 自检(已内置) `python try/human_drag.py` 断言五项:终点精确(<0.5px)、时长 0.4-2.5s、 速度起伏(vmax > vmin×5)、y 摆动(>2px)、存在回拉(x 序列非单调)。 10 样本联合烟测(solve → 换算 → 路径)全通过。 可视化:`python try/human_drag.py --plot` → `src/out/human_drag_path.png`(三种距离的轨迹)、`src/out/human_drag_xt.png`(x-t 曲线)。 ## 可调参数(按需) - 短距离(<100px):弓形幅值降到 ±8、过冲 ×0.3(小拖距人不甩) - `duration`:默认按距离自估算 0.6-1.4s;拖钮场景可传 `duration=800` 固定 - 未做:轨迹级鼠标事件 hook 采集真人样本对拍(当前参数为文献常识值, 如需过高级风控,用 CDP 录制 10 条真人轨迹做分布拟合替换)