This commit is contained in:
HuangHai
2026-01-14 09:56:04 +08:00
parent eed2b6f152
commit ee91bf76d2
14 changed files with 233 additions and 67 deletions

View File

@@ -1,10 +1,14 @@
# coding=utf-8
import asyncio
import logging
import asyncio
import os
import sys
import time
from Apps.TeLaiDian.Kit import take_screenshot, get_image_content_md5, clean_station_name, setup_logger
import cv2
from Apps.TeLaiDian.Kit import (
take_screenshot, get_image_content_md5, clean_station_name,
setup_logger, detect_price_click_point_cv, read_image, save_image
)
from Apps.TeLaiDian.ReadImageKit import ReadImageKit
from Apps.TeLaiDian.Service import TeLaiDianService
from Apps.TeLaiDian.Config.Setting import (
@@ -120,37 +124,92 @@ class TeLaiDianCrawler(BaseCrawler):
d.swipe_ext("up", scale=DETAIL_SCROLL_DISTANCE_RATIO)
await asyncio.sleep(1.5)
# 3. 点击“价格信息”按钮 (jgxx.jpg)
template_path = os.path.join(project_root, "Apps", "TeLaiDian", "Template", "jgxx.jpg")
logger.info(f"尝试点击价格详情按钮: {template_path}")
# 3. 点击“价格信息”区域 (识别橘红色价格 P0)
price_button_screen = take_screenshot(d, f"tld_before_price_click_{int(time.time())}.jpg")
logger.info("正在通过 CV 寻找橘红色价格区域 (P0)...")
click_point = detect_price_click_point_cv(price_button_screen)
# 调试:生成点击点标注图
if click_point:
debug_flag_path = price_button_screen.replace(".jpg", "_click_debug.jpg")
img_debug = read_image(price_button_screen)
if img_debug is not None:
cv2.circle(img_debug, (click_point[0], click_point[1]), 20, (0, 0, 255), -1) # 红色大圆点
cv2.line(img_debug, (click_point[0]-40, click_point[1]), (click_point[0]+40, click_point[1]), (255, 255, 255), 3)
cv2.line(img_debug, (click_point[0], click_point[1]-40), (click_point[0], click_point[1]+40), (255, 255, 255), 3)
save_image(debug_flag_path, img_debug)
logger.info(f"点击点调试图已保存: {debug_flag_path}")
try:
# 使用 uiautomator2 的图像识别点击
match = d.image.match(template_path)
if match:
logger.info(f"找到价格按钮,坐标: {match['point']}")
d.image.click(template_path)
if click_point:
logger.info(f"CV 成功定位价格区域,点击坐标: {click_point}")
d.click(click_point[0], click_point[1])
await asyncio.sleep(WAIT_DETAIL_PAGE_LOAD)
else:
logger.warning("未找到价格按钮模板,尝试备选方案:直接点击屏幕下方区域")
# 备选方案:如果模板匹配失败,尝试点击屏幕中下方
w, h = d.window_size()
d.click(w // 2, int(h * 0.8))
logger.warning("CV 未能定位价格区域,尝试模板匹配兜底...")
template_path = os.path.join(project_root, "Apps", "TeLaiDian", "Template", "jgxx.jpg")
match = d.image.match(template_path)
if match:
d.image.click(template_path)
else:
logger.warning("模板匹配也失败,执行坐标兜底...")
w, h = d.window_size()
d.click(w // 2, int(h * 0.45)) # 滑动后价格通常在屏幕中上部
await asyncio.sleep(WAIT_DETAIL_PAGE_LOAD)
except Exception as e:
logger.error(f"点击价格按钮失败: {e}")
logger.error(f"点击价格区域失败: {e}")
finally:
if os.path.exists(price_button_screen): os.remove(price_button_screen)
# 4. 截图并分析价格表
price_screen_path = take_screenshot(d, f"tld_detail_price_{int(time.time())}.jpg")
prices = await self.read_image_kit.analyze_detail_price(price_screen_path)
# 4. 循环滑动抓取完整分时电价
all_prices = []
last_price_md5 = None
price_page_count = 0
max_price_pages = 3 # 分时电价通常不会超过3页
logger.info("开始循环滑动抓取完整分时电价...")
while price_page_count < max_price_pages:
price_screen_path = take_screenshot(d, f"tld_detail_price_{int(time.time())}.jpg")
# 校验页面是否发生滚动变化
curr_md5 = get_image_content_md5(price_screen_path, top_ratio=0.2, bottom_ratio=0.2)
if curr_md5 == last_price_md5:
logger.info("价格页面内容无变化,判定已触底")
if os.path.exists(price_screen_path): os.remove(price_screen_path)
break
last_price_md5 = curr_md5
logger.info(f"正在分析价格详情页第 {price_page_count + 1} 页: {price_screen_path}")
page_prices = await self.read_image_kit.analyze_detail_price(price_screen_path)
if page_prices:
# 简单去重:根据时段合并
for p in page_prices:
if p not in all_prices:
all_prices.append(p)
# 向上滑动一点点,继续抓取
d.swipe_ext("up", scale=0.6)
await asyncio.sleep(1.5)
price_page_count += 1
# 清理临时截图
if os.path.exists(price_screen_path): os.remove(price_screen_path)
# 5. 保存数据
if prices:
if all_prices:
station_name_clean = clean_station_name(station_name)
logger.info(f"场站 {station_name_clean} 提取到 {len(prices)} 条价格信息,准备保存...")
await self.service.save_station_data(station_name_clean, address, prices)
# 对价格按时间排序
try:
all_prices.sort(key=lambda x: x.get('start', '00:00'))
except:
pass
logger.info(f"✅ 场站 {station_name_clean} 共提取到 {len(all_prices)} 条价格信息,准备保存...")
await self.service.save_station_data(station_name_clean, address, all_prices)
else:
logger.warning(f"未能从 {price_screen_path} 提取到价格信息")
logger.warning(f"❌ 未能提取到任何价格信息,请检查页面识别逻辑")
# 清理临时截图
for p in [first_screen_path, price_screen_path]: