Files
aiData/Doc/4、流程说明.md
HuangHai b66f683dfb 'commit'
2026-01-12 07:49:18 +08:00

162 lines
9.4 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.

# 新电途微信小程序爬虫流程说明文档
本文档详细记录了“新电途”微信小程序爬虫的工作流程、技术细节、避坑指南及维护策略,旨在帮助开发人员快速理解系统架构,应对 UI 变更及设备更换等场景。
---
## 1. 项目概述
本项目是一个基于 **Python + uiautomator2 + 视觉大模型 (VL) + 计算机图形学 (CV)** 的自动化爬虫系统。
其核心目标是模拟用户操作,遍历“新电途”小程序的场站列表,识别场站信息(名称、价格、空闲桩数),并进入详情页抓取地址和分时电价。
### 核心技术栈
* **控制端**: `uiautomator2` (Android 自动化控制)
* **视觉识别 (VL)**: 阿里云 Qwen-VL / DeepSeek (负责复杂的文字和语义提取)
* **计算机图形学 (CV)**: OpenCV (负责快速、低成本的广告检测、按钮定位、图像切片)
* **数据存储**: Doris (MySQL 协议) + Redis (去重缓存) + Huawei OBS (图片存储)
---
## 2. 核心流程图解
```mermaid
graph TD
A[启动程序 (Y5_Full)] --> B[Step 0: 初始化服务 & 数据库]
B --> C[Step 1: 打开小程序 (Y2)]
C --> D{检测广告 (CV)}
D -- 有广告 --> E[点击关闭 (CV模板/几何)]
D -- 无广告 --> F[Step 2: 列表页爬取 (Y3)]
E --> F
F --> G[截图 & 切片 (CV)]
G --> H[上传图片至 OBS]
H --> I[调用 VL 模型解析场站列表]
I --> J{是否存在有效场站?}
J -- 是 --> K[遍历场站]
K --> L{是否已处理? (Redis)}
L -- 否 --> M[点击进入详情页]
M --> N[截图详情页 & 解析地址]
N --> O[抓取分时电价 (可选)]
O --> P[返回列表页]
P --> K
L -- 是 --> K
J -- 否 --> Q[下拉翻页]
Q --> F
Q -- 结束 --> R[Step 3: 坐标回填 (Y4)]
R --> S{Cookie 有效?}
S -- 是 --> T[高德 API 查询 & 更新 DB]
S -- 否 --> U[弹窗手动登录 (T2)]
U --> T
```
---
## 3. 详细步骤与技术实现
### 步骤 0: 初始化 (`Y5_Full.py`)
* **动作**: 程序启动时,首先初始化 `XinDianTuService` 并连接数据库。
* **目的**: 确保后续爬取过程中数据库连接立即可用,避免在写入数据时才初始化导致的延迟或错误。
* **注意**: 数据库连接采用单例模式或长连接,程序结束时需显式关闭。
### 步骤 1: 启动小程序 (`Y2_OpenXinDianTu.py`)
* **动作**:
1. 强制停止微信并重新启动,确保回到微信主页。
2. 点击底部“微信”标签。
3. 点击右上角“搜索”按钮(基于百分比坐标)。
4. 输入“新电途”并点击搜索结果进入小程序。
* **坑点**:
* **搜索入口坐标**: 搜索按钮的位置在不同分辨率手机上略有差异,目前使用 `w * 0.84`, `h * 0.08`。如果更换手机,需检查此坐标。
* **输入法干扰**: `uiautomator2` 的输入法有时会失效,代码中做了 `send_keys(clear=True)` 和直接输入的双重兜底。
### 步骤 2: 列表页爬取 (`Y3_StationList.py`)
这是最核心的循环步骤(如需广告检测会自动调用 CV 模块):
1. **截图**: `d.screenshot()`。
2. **图像切片 (CV)**: 调用 `Kit.crop_cards_from_image`。
* 利用**行扫描算法**,根据像素亮度和方差,识别出场站卡片之间的灰色分割线,将大图切割成一个个小卡片 (`_vl.jpg` 实际上是带绿框的整图,配合 JSON 元数据)。
* **优势**: 极大降低了 VL 模型的理解难度,模型只需识别框内内容,不用自己找位置。
3. **VL 解析**: 将图片上传 OBS,调用 VL 模型(Qwen/DeepSeek)。
* **Prompt**: 要求模型提取“场站名称”、“价格”、“枪数”、“距离”等。
4. **坐标映射与安全检查**:
* 模型返回的坐标会结合 `device_info` (设备真实宽高) 进行校验。
* **Config.py**: `SAFE_EXCLUDE_RATIO=0.20` (顶部20%不点),`BOTTOM_SAFE_EXCLUDE_RATIO=0.1` (底部10%不点)。**这解决了第一屏场站无法点击的问题。**
### 步骤 3: 坐标回填 (`Y4_Coord.py`)
* **动作**: 扫描数据库中 `address` 不为空但 `coord_x/y` 为空的记录,调用高德地图 API 补全经纬度。
* **智能修复**:
* 任务开始前会自动探测 Cookie 有效性。
* 如果 Cookie 失效,自动调用 `T2_GetAmapCookie` 弹出浏览器窗口让用户手动登录。
* 登录完成后,程序自动保存新 Cookie 并继续执行回填任务,无需重启。
---
## 4. 技术选型指南:何时用 CV?何时用 VL?
| 场景 | 推荐技术 | 理由 |
| :--- | :--- | :--- |
| **广告关闭按钮** | **CV (OpenCV)** | 图标固定、形状简单(圆/叉)、要求毫秒级响应、零成本。 |
| **隐私协议按钮** | **CV (OpenCV)** | 颜色特征明显(黑色块)、位置固定。 |
| **场站卡片切分** | **CV (行扫描)** | 列表页结构规律,分割线特征稳定,CV 切分极其精准且快。 |
| **读取场站名称** | **VL (大模型)** | 字体多样、可能有干扰、OCR 传统方案容易错乱,VL 语义理解能力强。 |
| **读取价格/枪数** | **VL (大模型)** | 格式复杂("快 闲4/4"),VL 能直接提取结构化 JSON。 |
| **寻找"全部时段"** | **CV (几何特征)** | 按钮只有文字没有边框,但左右空白特征明显,用边缘检测更可靠。 |
**总结**:
* **结构固定、特征简单、追求速度** -> 用 CV。
* **语义复杂、内容多变、容错率高** -> 用 VL。
---
## 5. 关键参数配置 (`Config/Config.py`)
| 参数名 | 当前值 | 说明 | 调整场景 |
| :--- | :--- | :--- | :--- |
| `SAFE_EXCLUDE_RATIO` | **0.20** | **顶部**安全排除比例。在此比例内的坐标不点击。 | 如果发现第一屏顶部的场站无法点击,**调小**此值。 |
| `BOTTOM_SAFE_EXCLUDE_RATIO` | **0.1** | **底部**安全排除比例。避开“扫码充电”等按钮。 | 如果频繁误触底部按钮,**调大**此值。 |
| `SCROLL_DISTANCE_RATIO` | **0.3** | 翻页滑动幅度的比例。 | 如果翻页太快漏数据,**调小**;翻页太慢,**调大**。 |
| `MAX_CRAWL_DISTANCE` | **50** | 最大爬取距离 (km)。 | 根据业务需求调整爬取范围。 |
| `VL_MODEL_NAME` | `qwen3-vl-flash` | 使用的视觉模型版本。 | 如果识别准确率下降,可尝试升级模型。 |
---
## 6. 避坑指南与设备更换
### Q1: 更换手机需要注意什么?
1. **分辨率差异**:
* 代码大部分使用了**相对坐标 (百分比)**,理论上兼容不同分辨率。
* **重点检查**: `Y2_OpenXinDianTu.py` 中的搜索按钮点击坐标 (`0.84, 0.08`)。新手机可能刘海屏高度不同,导致 Y 轴偏移。
2. **截图尺寸 vs 屏幕尺寸**:
* 某些手机截图分辨率与 `d.info` 获取的屏幕分辨率不一致(如 Retina 缩放)。
* **解决方案**: `Y3_StationList.py` 中已包含自动检测逻辑:`if img_w != w: device_info['width'] = img_w`。一般无需手动修改。
### Q2: 小程序 UI 大改版怎么办?
1. **列表样式变更**:
* 如果列表不再有明显的灰色分割线,`Kit.py` 的切片逻辑 (`get_card_segments`) 会失效。
* **应对**: 修改 `Kit.py`,改用 VL 模型直接进行全图目标检测 (Object Detection),或者调整 CV 分割线的颜色阈值。
2. **广告样式变更**:
* 如果出现新广告,截图保存到 `Tools/Templates`,命名为 `ad_close_new.jpg` 即可,代码会自动加载。
### Q3: 为什么有场站但没点进去?
1. **被安全区拦截**: 检查日志 `safe exclude` 关键字。如果是误拦截,调整 `SAFE_EXCLUDE_RATIO`。
2. **VL 识别失败**: 检查 `Images/` 目录下的 `_vl.jpg`,看绿框是否框住了场站。如果没有,说明 CV 切片参数 (`Kit.py`) 需要调整(如 `row_mean` 阈值)。
---
## 7. 调试与单元测试
虽然本项目没有使用标准的 `unittest` 框架,但每个核心模块都设计了**独立运行入口 (`if __name__ == "__main__":`)**,方便单步调试。
### 单元调试清单
| 功能模块 | 对应文件 | 调试方法 | 作用 |
| :--- | :--- | :--- | :--- |
| **广告检测 (CV)** | `Util/Kit.py` | 无法直接运行,需编写临时脚本调用 `detect_ad_close_x`。建议在 `Tools/T3_CropCards.py` 中添加测试代码。 | 测试新广告模板是否生效。 |
| **切片逻辑 (CV)** | `Tools/T3_CropCards.py` | 运行该脚本,会自动读取 `Samples` 目录下的图片进行切片测试。 | 调试列表分割线识别参数。 |
| **进入小程序** | `Tools/Y2_OpenXinDianTu.py` | 直接运行该文件。 | 测试微信启动和搜索流程。 |
| **列表爬取主循环** | `Tools/Y3_StationList.py` | 直接运行该文件 (需连接手机)。 | 独立测试爬取流程,不依赖外部调度。 |
| **全流程** | `Tools/Y5_Full.py` | 直接运行该文件。 | 生产环境模拟(含坐标回填)。 |
### 建议的开发习惯
1. **遇到新 Bug**: 先看日志 (`Tools/Logs/`), 确定是哪一步卡住。
2. **保留现场**: `KEEP_SCREENSHOTS = True` (在 `Y3_StationList.py`),开启后会保留所有截图,方便事后分析。
3. **单步验证**: 如果是广告检测问题,把截图拿出来,写个几行的 Python 脚本单独调用 `Kit.detect_ad_close_x` 进行验证,不要每次都跑全流程。