162 lines
9.4 KiB
Markdown
162 lines
9.4 KiB
Markdown
|
|
# 新电途微信小程序爬虫流程说明文档
|
|||
|
|
|
|||
|
|
本文档详细记录了“新电途”微信小程序爬虫的工作流程、技术细节、避坑指南及维护策略,旨在帮助开发人员快速理解系统架构,应对 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` 进行验证,不要每次都跑全流程。
|