From a3347b9340e5b0106f8b388a7d9e105ec78df0bb Mon Sep 17 00:00:00 2001 From: HuangHai <10402852@qq.com> Date: Wed, 14 Jan 2026 07:05:03 +0800 Subject: [PATCH] 'commit' --- Apps/YeLiTe/ClearHistory.py | 4 +- Apps/YeLiTe/Crawler.py | 9 +-- Apps/YeLiTe/ReadImageKit.py | 5 +- Apps/YeLiTe/Service.py | 3 +- .../__pycache__/Crawler.cpython-310.pyc | Bin 0 -> 8563 bytes .../YeLiTe/__pycache__/Opener.cpython-310.pyc | Bin 0 -> 2424 bytes .../__pycache__/ReadImageKit.cpython-310.pyc | Bin 6313 -> 6530 bytes .../__pycache__/Service.cpython-310.pyc | Bin 0 -> 4303 bytes T3_YeLiTe_DirectCrawl.py | 59 ------------------ 9 files changed, 14 insertions(+), 66 deletions(-) create mode 100644 Apps/YeLiTe/__pycache__/Crawler.cpython-310.pyc create mode 100644 Apps/YeLiTe/__pycache__/Opener.cpython-310.pyc create mode 100644 Apps/YeLiTe/__pycache__/Service.cpython-310.pyc delete mode 100644 T3_YeLiTe_DirectCrawl.py diff --git a/Apps/YeLiTe/ClearHistory.py b/Apps/YeLiTe/ClearHistory.py index 631cbf4..e495276 100644 --- a/Apps/YeLiTe/ClearHistory.py +++ b/Apps/YeLiTe/ClearHistory.py @@ -3,6 +3,8 @@ import os import sys import logging +from Util import Win32Patch + # 确保项目根目录在 sys.path 中 project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) if project_root not in sys.path: @@ -16,7 +18,7 @@ from Config.Config import DB_URL # 配置日志 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger("ClearYeLiTe") - +Win32Patch.patch() async def main(): """ 根据注释完成代码: diff --git a/Apps/YeLiTe/Crawler.py b/Apps/YeLiTe/Crawler.py index 58b4fdc..a958a4c 100644 --- a/Apps/YeLiTe/Crawler.py +++ b/Apps/YeLiTe/Crawler.py @@ -105,6 +105,7 @@ class YiLaiTeCrawler(BaseCrawler): break name = station.get('name') + address = station.get('address') point = station.get('point') if not name or not point: @@ -217,7 +218,7 @@ class YiLaiTeCrawler(BaseCrawler): # -------------------------------------------------- # 启动后台任务处理详情页 - task = asyncio.create_task(self.analyze_detail_background(name, detail_shots)) + task = asyncio.create_task(self.analyze_detail_background(name, detail_shots, address=address)) background_tasks.append(task) processed_count += 1 @@ -262,7 +263,7 @@ class YiLaiTeCrawler(BaseCrawler): return processed_count - async def analyze_detail_background(self, station_name, image_paths): + async def analyze_detail_background(self, station_name, image_paths, address=None): """ 后台异步分析详情页 (支持多张截图合并) """ @@ -298,8 +299,8 @@ class YiLaiTeCrawler(BaseCrawler): unique_prices.sort(key=lambda x: x.get('start', '00:00')) if unique_prices: - await self.service.process_price_detail_data(station_name, unique_prices) - logger.info(f"场站 {station_name} 价格分析完成并入库 (记录数: {len(unique_prices)})") + await self.service.process_price_detail_data(station_name, unique_prices, address=address) + logger.info(f"场站 {station_name} 价格分析完成并入库 (记录数: {len(unique_prices)}, 地址: {address})") else: logger.warning(f"场站 {station_name} 未识别到价格信息") diff --git a/Apps/YeLiTe/ReadImageKit.py b/Apps/YeLiTe/ReadImageKit.py index 06adba7..9f2a901 100644 --- a/Apps/YeLiTe/ReadImageKit.py +++ b/Apps/YeLiTe/ReadImageKit.py @@ -152,9 +152,10 @@ class ReadImageKit: 每个对象包含: - "name": 场站名称 (仅提取名称文字) + - "address": 场站地址 (通常在名称下方,请完整提取地址文字) - "is_valid": true/false (是否为真实的场站卡片) - 注意:仅提取名称,不要包含距离或价格。 + 注意:仅提取名称和地址,不要包含距离或价格。 """ else: # 如果 CV 没检测到,退回到纯 VLM 模式 @@ -163,6 +164,7 @@ class ReadImageKit: 忽略顶部的筛选栏,仅提取下方重复出现的场站卡片。 输出格式为 JSON 数组,每个对象包含: - "name": 场站名称 + - "address": 场站地址 (请仔细识别并提取场站的详细地址信息) - "point": 场站卡片的中心点击坐标 [x, y] - "bbox": 场站卡片的边界框 [x1, y1, x2, y2] """ @@ -184,6 +186,7 @@ class ReadImageKit: if res and (res.get("is_valid") is True or (res.get("name") and res.get("is_valid") is not False)): final_stations.append({ "name": res.get("name"), + "address": res.get("address"), "point": [(bbox[0] + bbox[2]) // 2, (bbox[1] + bbox[3]) // 2], "bbox": bbox }) diff --git a/Apps/YeLiTe/Service.py b/Apps/YeLiTe/Service.py index 23baddb..681997c 100644 --- a/Apps/YeLiTe/Service.py +++ b/Apps/YeLiTe/Service.py @@ -90,7 +90,7 @@ class YiLaiTeService: except Exception as e: logger.error(f"更新任务结束日志失败: {e}") - async def process_price_detail_data(self, station_name, hourly_schedule) -> bool: + async def process_price_detail_data(self, station_name, hourly_schedule, address=None) -> bool: """ 直接保存已处理好的小时段价格数据 """ @@ -109,6 +109,7 @@ class YiLaiTeService: station_hash=station_hash, operator=self.operator, station_name=station_name, + address=address, valid_start_time=now ) diff --git a/Apps/YeLiTe/__pycache__/Crawler.cpython-310.pyc b/Apps/YeLiTe/__pycache__/Crawler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f33f0e5339b0354fb32184f010e2fc57c8d9f3c GIT binary patch literal 8563 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 j!evZ!C>K9`CKM7AX;_0IlKaW*Z* zjJA|c3acx)5tRt7wOurWAc$Ui=y@J`a(2~+B7&ZTn(-37bRPca{Quwie&-xGGu1b1 zq^QRuB75vTbxAeKo=ZOf8eia#d7KH?O{g?8pSd#t=Q3r&CkFP@RZk0;v_UFo_t;HZ z4UI^HFhqY!4RDLrdaL^qhDcb$HGs8*;U??=G@{n#3pmEd1i~8(!2@s^X=4Nvz6ixc zB51=znD)qA3S_B_njQt(eM_Bf84mbPl^vnvHel-)(hR!Z=@^eOyRvclUMb&-f*WXpEj9DxJx-=a+`-};CzCBBSBkMw%Jm_XosAj z-WHQ8+h4%}S`z4B8=bI*-VRh2AGepdU&>>!h%sh?`OPxo8sozv{SpX>Jd^_lajlr6 z-vS+>eGbTVu`{geU5e2Wm9;ttb(BVe6FW?AeRsEBbyjIoPb*a?R6}#_pfOGl-5IKd JWAuFJ*_y!prv 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 eiB0xB#IiW)CU21ezl7i!TH94dnm$8F4@W literal 0 HcmV?d00001 diff --git a/T3_YeLiTe_DirectCrawl.py b/T3_YeLiTe_DirectCrawl.py deleted file mode 100644 index 521df81..0000000 --- a/T3_YeLiTe_DirectCrawl.py +++ /dev/null @@ -1,59 +0,0 @@ -# coding=utf-8 -import sys -import os -import asyncio -import logging - -# 将项目根目录添加到 sys.path -project_root = os.path.dirname(os.path.abspath(__file__)) -if project_root not in sys.path: - sys.path.append(project_root) - -# 配置日志 -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) -logger = logging.getLogger("T3_YeLiTe_DirectCrawl") - -try: - from Apps.YeLiTe.Service import YiLaiTeService - from Apps.YeLiTe import Crawler, Kit -except Exception as e: - logger.error(f"❌ 初始化导入失败: {e}") - sys.exit(1) - -async def run_process(): - logger.info("=== 开始直接爬取任务 (驿来特) ===") - logger.info("注意:请确保手机当前停留在【驿来特】小程序的场站列表页!") - - # 步骤 0: 初始化基础服务 - logger.info(">>> 步骤 0: 初始化基础服务 (数据库连接)...") - service = YiLaiTeService() - await service.init_db() - - try: - # 启动前清空临时目录 - Kit.clear_temp_dir() - - # 步骤 2: 执行爬取任务 - logger.info(">>> 步骤 1: 开始执行场站爬取任务 (跳过启动步骤)...") - # 调用 Crawler 的 main,并传入 service - await Crawler.main(service=service) - - logger.info("✅ 爬取任务完成!") - - except Exception as e: - logger.error(f"❌ 运行异常: {e}") - finally: - if service: - await service.close_db() - logger.info("=== 任务结束 ===") - -if __name__ == "__main__": - try: - asyncio.run(run_process()) - except KeyboardInterrupt: - logger.info("\n🛑 用户手动停止了程序 (Ctrl+C)。") - except Exception as e: - logger.exception(f"主程序崩溃: {e}")