9.8 KiB
9.8 KiB
滑块验证模块
本模块实现了豆瓣登录页面滑块验证码的自动检测和解决功能。
功能特性
- ✅ 自动检测滑块验证码中的缺口位置
- ✅ 支持多滑块检测(检测两个滑块并计算距离)
- ✅ 模拟人类滑动轨迹(贝塞尔曲线)
- ✅ 自动重试机制(最多 10 次)
- ✅ 滑块浮窗消失判定验证成功
目录结构
src/slider/
├── cli.ts # 命令行工具,用于批量评估/标注
├── index.ts # 模块导出
├── types.ts # 类型定义
├── detector.ts # 主滑块检测器
├── detector-self-learning.ts # 自学习第二滑块检测
├── slider-controller.ts # 滑块移动控制器
├── validator.ts # 检测结果验证工具
├── detection/
│ └── candidate-search.ts # 候选区域搜索算法
└── utils/
├── geometry.ts # 几何计算工具
└── image.ts # 图像处理工具
运行输出约定
- 登录流程截取的原始验证码保存在项目根目录的
noflag/ - 自动检测产生的标注结果保存在根目录的
output/ - 可执行
npm run slider -- --pic-dir=noflag对原始截图批量复核,结果同样输出至output/
核心算法
1. 滑块检测 (detector.ts)
- 多策略候选搜索:暗区域检测、边缘检测、颜色量化、LAB 色彩空间检测
- 候选框评分:基于形状、色调一致性、内部边缘密度、梯度平滑度
- 边缘精炼:使用 Sobel 边缘检测和投影分析精确定位滑块边界
2. 第二滑块检测 (detector-self-learning.ts)
- 模板匹配:使用第一个检测到的滑块作为模板
- 边缘模板:对图像和模板进行 Canny 边缘检测后匹配
- 位置验证:确保第二个滑块在同一水平线上(y 轴偏差 < 25px)
3. 滑动控制 (slider-controller.ts)
- 距离计算(v1.1.0 简化算法):
- 双滑块模式:
距离 = (缺口X - 滑块X) / scaleX- 检测到左侧滑块(b1)和右侧缺口(b2)
- 计算两者左边界的水平距离
- 除以图像缩放比例(原始 340px → 检测用 800px)
- 原理:类比"两只小鸟嘴尖的水平距离"
- 单滑块模式:
距离 = 缺口中心X / scaleX- 仅检测到缺口位置时的兜底方案
- 从起始位置直接滑动到缺口中心
- 双滑块模式:
- 图像缩放优化:
- 原始验证码宽度:340px
- 放大到 800px 进行检测(scaleX ≈ 2.35)
- 提高小尺寸滑块的检测精度
- 拟人化滑动:
- 使用 Playwright 的
steps参数 - 平滑移动轨迹,避免机器人特征
- 使用 Playwright 的
使用方法
1. 环境变量配置
# 启用自动滑块验证
export DOUBAN_AUTO_SLIDER=1
# 设置手机号
export DOUBAN_PHONE=13800138000
# 运行登录脚本
npm run login
2. 编程接口
import { SliderController } from './slider';
import { Page } from 'playwright';
const controller = new SliderController(10); // 最多尝试 10 次
const result = await controller.solveSlider(
page,
'.tcaptcha_drag_button', // 滑块按钮选择器
'#tcaptcha_iframe' // 验证码容器选择器
);
if (result.success) {
console.log(`验证成功!尝试 ${result.attempts} 次`);
} else {
console.log('验证失败');
}
3. 独立使用滑块检测器
import { SliderDetector } from './slider';
const detector = new SliderDetector();
const boxes = await detector.detectSlider(
'captcha.png',
'output/captcha-annotated.png',
true
);
if (boxes && boxes.length > 0) {
console.log('检测到滑块:', boxes);
}
4. CLI 工具
npm run slider -- --pic-dir=images/douban
- 默认读取
images/douban下的验证码图片并输出标注结果到images/output - 若存在
ground-truth.json,会自动评估检测精度和召回率 - 通过
--pic-dir=子目录可切换其他图片集合
工作流程
- 等待滑块出现:检测页面中是否存在滑块验证码 iframe
- 截图:捕获验证码区域图像,保存原始图到
noflag/目录 - 图像预处理:将图像缩放到 800px 宽度以提高检测精度
- 多策略检测:并行运行四种算法检测滑块候选框
- 暗区域检测(基于亮度阈值)
- Canny 边缘检测
- 颜色量化(K-means 聚类)
- LAB 色彩空间分析
- 候选框评分与筛选:
- 计算每个候选框的综合分数(形状、颜色、边缘)
- IoU 去重,合并重叠候选框
- 选择得分最高的两个滑块
- 距离计算:
- 双滑块:
(b2.x - b1.x) / scaleX - 单滑块:
b.x / scaleX
- 双滑块:
- 可视化标注:在检测图上绘制红色框,保存到
output/目录 - 模拟滑动:拖动左侧滑块到计算出的距离
- 验证结果:检查是否出现
.tc-success成功标识 - 失败重试:点击刷新按钮,重新截图检测(最多 10 次)
参数说明
SliderController 构造函数
new SliderController(maxAttempts: number = 10)
maxAttempts: 最大尝试次数,默认 10 次
solveSlider 方法
async solveSlider(
page: Page,
sliderSelector: string = '.tcaptcha_drag_button',
captchaSelector: string = '#tcaptcha_iframe'
): Promise<SliderSolveResult>
page: Playwright 页面对象sliderSelector: 滑块按钮的 CSS 选择器captchaSelector: 验证码容器的 CSS 选择器
返回值 SliderSolveResult
interface SliderSolveResult {
success: boolean; // 是否成功
attempts: number; // 尝试次数
distance?: number; // 滑动距离(像素)
}
依赖项
sharp: 图像处理库,用于边缘检测、颜色量化等playwright: 浏览器自动化,用于截图和鼠标操作
注意事项
- 选择器适配:不同网站的滑块选择器可能不同,需要根据实际情况调整
- 截图位置:临时截图保存在
os.tmpdir()/douban-slider/目录 - 成功判定:通过检查验证码浮窗是否消失来判断验证是否成功
- 失败处理:自动验证失败后会提示用户手动完成
调试
如需查看检测过程中的日志,观察控制台输出:
[SliderController] 开始滑块验证,最多尝试 10 次
[SliderController] 等待验证码 iframe 加载...
[SliderController] 验证码 iframe 已加载
[SliderController] 等待滑块背景图加载...
[SliderController] 滑块背景图已加载
[SliderController] ===== 第 1/10 次尝试 =====
[SliderController] 已截图到: /Users/gavin/douban-login/noflag/captcha-20250125-123456.png
[SliderDetector] 图像已缩放: 340x191 -> 800x449 (scaleX=2.35)
[SliderDetector] 检测到 2 个滑块候选框
[SliderDetector] 滑块 1: x=45, width=60, score=0.85
[SliderDetector] 滑块 2: x=195, width=55, score=0.82
[SliderDetector] 已保存标注图: /Users/gavin/douban-login/output/captcha-20250125-123456-detected.png
[SliderController] ✓ 检测到 2 个滑块
[SliderController] 计算距离: (195 - 45) / 2.35 = 63.8px
[SliderController] 开始拖动滑块 64px
[SliderController] ✓ 滑块验证成功!(1000ms后窗口消失)
[SliderController] 验证成功!共尝试 1 次
关键日志说明:
图像已缩放: 显示原始尺寸、检测尺寸和缩放比例检测到 N 个滑块候选框: N=2 表示双滑块模式,N=1 表示单滑块模式滑块 1/2: 显示每个滑块的 x 坐标、宽度和评分已保存标注图: 红框标注结果的保存路径计算距离: 显示详细的距离计算公式✓ 滑块验证成功: 检测到腾讯验证码的成功标识
故障排查
1. 检测不到滑块
症状:日志显示"未检测到滑块"
排查步骤:
- 检查
noflag/目录下的原始截图是否正确 - 确认验证码已完全加载(等待 iframe 和图片元素)
- 查看
output/目录的标注图,确认候选框是否被正确识别 - 调整
candidate-search.ts中的检测阈值
2. 滑动距离不准确
症状:滑块滑过头或不够远
排查步骤:
- 查看日志中的
scaleX值(应该约为 2.35) - 确认使用的是双滑块模式还是单滑块模式
- 检查
output/目录标注图,红框是否准确框住滑块 - 验证距离计算公式:
(b2.x - b1.x) / scaleX
v1.1.0 改进:
- 简化了距离计算逻辑,移除复杂的坐标转换
- 采用"两只小鸟距离"原理,直接计算左边界差值
3. 验证总是失败
症状:滑动后没有出现成功提示
可能原因:
- 滑动距离计算错误(参见上一条)
- 触发反爬虫检测(轨迹太机械)
- 网络延迟导致成功标识未及时显示
解决方案:
- 检查日志中的滑动距离是否合理(通常 50-150px)
- 增加成功判定的等待时间(当前 1000ms)
- 尝试多次重试(当前最多 10 次)
- 查看浏览器开发者工具,确认
.tc-success类名是否出现
4. 视觉调试技巧
查看检测结果:
- 运行登录后,打开
output/目录 - 找到最新的
*-detected.png文件 - 检查红框是否准确标注了滑块和缺口
- 对比
noflag/目录的原始图,确认缩放和标注的准确性
理想的标注结果:
- 左侧滑块:红框紧贴滑块边缘
- 右侧缺口:红框框住缺口区域
- 两个红框高度基本一致(y 坐标偏差 < 25px)
移植说明
本模块从 captcha_cracker 项目移植而来,并进行了以下扩展:
- 原样保留检测、标注、CLI 与验证器等核心能力
- 新增 Playwright 集成,用于自动截图和滑块拖动
- 添加登录流程的滑块控制器与重试机制
- 调整脚本入口与文档,便于在豆瓣登录场景复用