Files
aiData/Apps/XinDianTu/Doc/流程说明.md
HuangHai f4b398227e 'commit'
2026-01-12 16:54:44 +08:00

7.2 KiB
Raw Blame History

新电途数据抓取流程说明文档

本文件详细记录了新电途(微信小程序)数据抓取爬虫的架构、抓取流程与关键实现,包含识别方法、异步并发设计、去重策略、鲁棒性与持久化方案,便于后续研发人员理解与扩展。


1. 项目概述

目标是自动采集新电途小程序中的充电场站信息:场站名称、价格与会员价、充电枪状态、距离、停车与标签信息、地址,以及 24 小时分时价格表。


2. 核心抓取流程

爬虫采用“列表 -> 详情 -> 价格 -> 返回 -> 列表”的闭环流程;UI 操作与视觉识别解耦,视觉识别在后台异步进行。

2.1 列表页

  • 操作:每屏截取一张大图。
  • 图形学切片:使用 OpenCV 对列表截图进行卡片分割,生成卡片 JSON 元数据与标注图 _vl.jpg(绿色框标注卡片区域),并提取每个卡片的可点击像素坐标 click_point。
  • VL 模型识别:调用 ReadImageKit.parse_vl_image(vl_img_path, json_metadata) 对 _vl.jpg 进行识别,返回按从上到下顺序排列的场站语义信息(名称、价格、枪数、标签、距离等)。
  • 匹配与点击:将几何卡片与 VL 结果按顺序对齐,读取卡片的 click_point,点击进入详情页。点击使用像素坐标的整数值,确保跨设备稳定。
  • 去重策略:仅按“列表显示的场站名称”进行去重;名称先经 clean_station_name 去掉尾部省略号与空格,例如 AAABBB... → AAABBB;首次遇到该名称时处理并写入 Redis 键 crawled:xdt:{name},后续同名直接跳过。
  • 异步上传备查:截图 _vl.jpg 与详情页截图等通过后台线程池上传到 OBS,以便后续排查;UI 不等待上传完成。

2.2 详情页

  • 进入:点击列表页卡片。
  • 操作:截取详情页全图。
  • 异步识别地址:调用 ReadImageKit.parse_address(station_name, detail_path) 后台提取完整场站名称与地址信息,处理完成后通过 XinDianTuService.backfill 回填 Profile。
  • 入口定位:查找“全部时段”入口(图形学模板匹配为主,必要时可通过 VL 辅助 find_all_time_button_coordinate),点击进入价格页。
  • 返回:完成入口点击与截图后,尽快返回列表页以继续 UI 流程。

2.3 价格页

  • 操作:对价格列表进行多次滑动与截图,使用 MD5 去重防止重复采集。
  • 异步识别价格:后台调用 ReadImageKit.parse_price_schedule_multi(station_name, images) 聚合并去重多图识别结果;随后使用 ReadImageKit.hourly_full_day(rows) 将不规则时段规整为 24 小时整点数据。
  • 持久化:通过 XinDianTuService.process_price_detail_data 直接保存小时级价格数据。
  • 返回:完成截图后立即返回上一级与列表页,保持 UI 流畅。

3. 关键技术点

3.1 识别方法(混合模式)

  • 图形学切片:确定每个卡片的点击中心与像素矩形,保证 UI 点击的几何准确性。
  • 视觉大模型(VL):Qwen-VL 负责卡片语义识别(名称、价格、枪数、距离、标签等),并提供名称与语义字段的稳健解析。
  • 广告与功能入口识别:若本地图形学未检测到广告,Opener.py 使用 VL 作兜底识别,通过设备 window_size 构造宽高,提升跨设备稳定性。

3.2 异步并发设计

  • 核心:UI 线程只负责点击/滑动/返回,视觉识别与上传在后台异步执行。
  • 实现:
    • 视觉识别:AsyncOpenAI 客户端异步调用;后台任务通过 asyncio.create_task 管理。
    • 上传备查:使用 loop.run_in_executor(None, uploader.upload_file, ...) 将 OBS 上传丢给线程池,避免事件循环类型不匹配导致卡住。
  • 好处:UI 流程不阻塞;整体采集吞吐提升显著。

3.3 本地路径输入与上传策略

  • 本地路径:VL 调用对本地图片自动转 base64 data URL,不再等待 OBS 上传完成,显著降低识别延迟。
  • 上传后台:仍将关键图片在后台上传到 OBS 作为备查资料,但不影响主流程推进。

3.4 坐标系与点击

  • VL 坐标:0–1000 归一化或 bounds_norm(0–1)。
  • UI 坐标:屏幕像素坐标(如 1080×2400)。
  • 点击策略:使用切片生成的像素坐标整数值 click_point,避免相对坐标在不同设备的偏移误差。
  • 安全排除:对过于靠近顶部或底部功能区的坐标进行安全排除(SAFE_EXCLUDE_RATIO 与 BOTTOM_SAFE_EXCLUDE_RATIO),降低误触风险。

3.5 去重策略

  • 仅按名称去重:clean_station_name 去掉尾部省略号与空格,首次遇到即记录 crawled:xdt:{name};后续同名直接跳过。
  • 设计原因:直观且符合业务语义,避免前缀/模糊匹配带来的误判。
  • 一致性:与艾特吉易充采用相同策略,保持跨项目行为一致。

3.6 鲁棒性设计

  • JSON 提取:ReadImageKit._extract_json 提升对杂乱文本的 JSON 提取能力。
  • 中文路径与空文件校验:Kit.py 统一图片读写与校验,避免并发读写导致崩溃。
  • 异步任务清理:每页结束清理已完成任务,最终 asyncio.gather 等待残余任务,防止内存堆积。

4. 数据持久化

  • Service 层统一落库:
    • process_station_list_vl:按站点生成 Profile 与 Status;解析枪数与当前价格。
    • process_price_detail_data:保存 24 小时规整后的价格数据。
    • process_station_address:回填完整名称与地址信息到 Profile。
  • Model 结构:StationProfile、StationStatus、StationPriceSchedule。

5. 配置与参数

  • 翻页距离:SCROLL_DISTANCE_RATIO 控制 swipe_ext("up") 的滑动尺度。
  • 等待时间:WAIT_DETAIL_PAGE_LOAD、WAIT_BACK_TO_LIST、WAIT_AFTER_SCROLL 控制页面加载与返回的等待时长。
  • Redis 过期:REDIS_STATION_EXPIRE 控制去重键的有效期。

6. 开发建议

  1. 异步 QPS:并发识别会提高 API QPS 需求,注意密钥配额与限流。
  2. 设备适配:宽高从 d.window_size() 获取,避免依赖可能缺失的设备信息键。
  3. 故障排查:保留关键截图并后台上传,便于复盘识别结果与 UI 流程。
  4. 性能优化:大型图片识别建议优先本地路径 + base64 方案,减少远程下载与上传等待。

7. 主要代码参考


文档更新日期:2026-01-12