Files
douban-login/src/slider/README.md
2025-10-25 23:39:25 +08:00

9.8 KiB
Raw Blame History

滑块验证模块

本模块实现了豆瓣登录页面滑块验证码的自动检测和解决功能。

功能特性

  • 自动检测滑块验证码中的缺口位置
  • 支持多滑块检测(检测两个滑块并计算距离)
  • 模拟人类滑动轨迹(贝塞尔曲线)
  • 自动重试机制(最多 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 参数
    • 平滑移动轨迹,避免机器人特征

使用方法

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=子目录 可切换其他图片集合

工作流程

  1. 等待滑块出现:检测页面中是否存在滑块验证码 iframe
  2. 截图:捕获验证码区域图像,保存原始图到 noflag/ 目录
  3. 图像预处理:将图像缩放到 800px 宽度以提高检测精度
  4. 多策略检测:并行运行四种算法检测滑块候选框
    • 暗区域检测(基于亮度阈值)
    • Canny 边缘检测
    • 颜色量化K-means 聚类)
    • LAB 色彩空间分析
  5. 候选框评分与筛选
    • 计算每个候选框的综合分数(形状、颜色、边缘)
    • IoU 去重,合并重叠候选框
    • 选择得分最高的两个滑块
  6. 距离计算
    • 双滑块:(b2.x - b1.x) / scaleX
    • 单滑块:b.x / scaleX
  7. 可视化标注:在检测图上绘制红色框,保存到 output/ 目录
  8. 模拟滑动:拖动左侧滑块到计算出的距离
  9. 验证结果:检查是否出现 .tc-success 成功标识
  10. 失败重试:点击刷新按钮,重新截图检测(最多 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: 浏览器自动化,用于截图和鼠标操作

注意事项

  1. 选择器适配:不同网站的滑块选择器可能不同,需要根据实际情况调整
  2. 截图位置:临时截图保存在 os.tmpdir()/douban-slider/ 目录
  3. 成功判定:通过检查验证码浮窗是否消失来判断验证是否成功
  4. 失败处理:自动验证失败后会提示用户手动完成

调试

如需查看检测过程中的日志,观察控制台输出:

[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. 视觉调试技巧

查看检测结果

  1. 运行登录后,打开 output/ 目录
  2. 找到最新的 *-detected.png 文件
  3. 检查红框是否准确标注了滑块和缺口
  4. 对比 noflag/ 目录的原始图,确认缩放和标注的准确性

理想的标注结果

  • 左侧滑块:红框紧贴滑块边缘
  • 右侧缺口:红框框住缺口区域
  • 两个红框高度基本一致y 坐标偏差 < 25px

移植说明

本模块从 captcha_cracker 项目移植而来,并进行了以下扩展:

  1. 原样保留检测、标注、CLI 与验证器等核心能力
  2. 新增 Playwright 集成,用于自动截图和滑块拖动
  3. 添加登录流程的滑块控制器与重试机制
  4. 调整脚本入口与文档,便于在豆瓣登录场景复用