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

295 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 滑块验证模块
本模块实现了豆瓣登录页面滑块验证码的自动检测和解决功能。
## 功能特性
- ✅ 自动检测滑块验证码中的缺口位置
- ✅ 支持多滑块检测(检测两个滑块并计算距离)
- ✅ 模拟人类滑动轨迹(贝塞尔曲线)
- ✅ 自动重试机制(最多 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. 环境变量配置
```bash
# 启用自动滑块验证
export DOUBAN_AUTO_SLIDER=1
# 设置手机号
export DOUBAN_PHONE=13800138000
# 运行登录脚本
npm run login
```
### 2. 编程接口
```typescript
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. 独立使用滑块检测器
```typescript
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 工具
```bash
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 构造函数
```typescript
new SliderController(maxAttempts: number = 10)
```
- `maxAttempts`: 最大尝试次数默认 10
### solveSlider 方法
```typescript
async solveSlider(
page: Page,
sliderSelector: string = '.tcaptcha_drag_button',
captchaSelector: string = '#tcaptcha_iframe'
): Promise<SliderSolveResult>
```
- `page`: Playwright 页面对象
- `sliderSelector`: 滑块按钮的 CSS 选择器
- `captchaSelector`: 验证码容器的 CSS 选择器
### 返回值 SliderSolveResult
```typescript
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. 调整脚本入口与文档便于在豆瓣登录场景复用