滑动验证不能直接用OpenCV模板匹配,因平台动态生成带噪声的背景图与缺口图,匹配成功率低;核心难点是模拟真实人类拖动轨迹并通过前端JS生成加密validate签名,需用Playwright/Selenium控制浏览器执行。

滑动验证为什么不能直接用 OpenCV 模板匹配?
因为主流购票平台(如 12306、携程)的滑块背景图和缺口图每次请求都会动态生成,且加入了像素级噪声、边缘模糊、色差扰动——cv2.matchTemplate 在无预处理时匹配成功率通常低于 30%,更别提后续拖动轨迹校验。
真正卡住的不是“找缺口”,而是“让服务器相信你是人”:滑块拖动距离要准,但速度曲线、加速度变化、中途微调、起停顿都要拟合真实手指操作。
- 缺口位置必须从带干扰的图中稳定提取,推荐用
Canny + HoughLinesP结合轮廓面积过滤,而非纯模板匹配 - 服务端会校验
X-Request-ID、tk等动态 token,滑动前必须完成完整登录态 + 行为前置埋点(比如鼠标悬停 800ms) - 返回的
validate字段不是 base64 图片,而是加密后的轨迹签名,必须用前端同源 JS 环境生成(即不能纯 Python 算)
必须复用浏览器环境生成轨迹签名
12306 和多数平台的滑动验证已弃用纯后端校验,validate 值由前端 JS 运行时基于鼠标事件时间戳、坐标序列、canvas 指纹等实时加密生成。Python 直接构造 JSON 提交必然返回 {"result": "fail", "reason": "invalid track"}。
可行路径只有一条:用 Playwright 或 Selenium 控制真实 Chromium 内核,在页面上下文中执行轨迹生成逻辑,再把结果传回 Python 主流程。
立即学习“Python免费学习笔记(深入)”;
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 用
page.evaluate()注入 JS 函数,接收 Python 计算出的坐标序列(如[[0,0],[12,5],[28,11],...]),输出加密后的validate字符串 - JS 中必须模拟真实 mousemove 频率(建议 40–60ms 间隔),并插入 1–2 次反向微调(如倒退 2px 后继续)
- 避免使用
page.drag_and_drop()这类高阶 API,它生成的是线性匀速轨迹,服务器一眼识别为 bot
真实轨迹算法的关键参数怎么定?
不是越像人越好,而是要在“像人”和“不触发行为风控”之间找平衡点。实测发现,以下三组参数组合在 12306 v2.12+ 上通过率最高:
- 总耗时控制在
1800–2400ms:短于 1500ms 易被判定为机器加速;长于 3000ms 可能超时或触发二次验证 - 加速度曲线用三次贝塞尔插值,控制点设为
(0.2, 0), (0.4, 1), (0.8, 0.8),比正态分布更接近拇指肌群响应 - 在行程 65%–75% 处插入一次
±3px的横向抖动(非随机,固定偏移),可绕过部分基于轨迹平滑度的模型拦截
示例片段(JS 端):
function generateTrack(x, y, duration = 2100) {
const points = [];
const steps = Math.floor(duration / 50);
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const x1 = bezier(t, 0, 0.2, 0.4, 0.8) * x;
const y1 = bezier(t, 0, 0, 1, 0.8) * y;
points.push([Math.round(x1), Math.round(y1)]);
}
// 插入抖动
const mid = Math.floor(points.length * 0.7);
points[mid][0] += 2;
return points;
}验证码图像下载与缺口识别的稳定处理
直接请求 https://xxxx.com/api/pic 得到的图大概率是带水印、缩放失真或 CDN 缓存旧图。必须确保拿到的是当前会话绑定的原始图。
- 从浏览器中捕获
<img>标签的src属性前,先等待document.querySelector('.yidun_bg-img').complete === true - 用
page.screenshot(path='bg.png', clip={x, y, width, height})截取渲染后 DOM 区域,比 HTTP 下载更可靠 - 缺口识别不用完整 CNN,
cv2.threshold+cv2.findContours足够:对灰度图做 OTSU 二值化,找最大白色连通域的左边界 x 坐标,误差 ±2px 内即可
注意:缺口实际偏移量 ≠ 图像像素差,需按页面 getBoundingClientRect() 换算为相对于滑块容器的 clientX 值,再减去滑块自身宽度的一半。
真正难的不是算出拖多远,而是让整个链路里每个环节都“不突兀”:token 时效、鼠标移动节奏、图片加载顺序、JS 执行时机——漏掉任意一环,validate 就是无效字符串。

















