# site_shunxin.py import os import re from datetime import datetime import pandas as pd def _close_tab(page, tab_name): """关闭指定名称的标签页(Ant Design Tabs)。 通过标签文字定位标签容器,点击其右侧的关闭(×)按钮。 - tab_name 采用包含匹配,对“ 运单列表”这类带空格的标签同样有效。 - 关闭失败不会影响主流程,仅打印提示信息。 """ try: tab = page.locator(".ant-tabs-tab").filter( has=page.get_by_role("tab", name=tab_name) ) if tab.count() == 0: print( f" ℹ️ 未找到标签页【{tab_name.strip()}】(可能尚未打开或已关闭),跳过。" ) return tab.first.locator(".ant-tabs-tab-remove").click() print(f" 🗙 已关闭标签页【{tab_name.strip()}】") page.wait_for_timeout(300) except Exception as e: print(f" ⚠️ 关闭标签页【{tab_name.strip()}】时出错: {e}") def shunxin_expected_download(page): """顺心:应到货物数据下载""" print("\n▶ 开始执行【顺心 - 应到货物数据下载】任务...") # 初始化并创建下载目录 download_dir = os.path.join(os.getcwd(), "downloads") if not os.path.exists(download_dir): os.makedirs(download_dir) print(f">> 已创建专属下载文件夹: {download_dir}") export_times = [] target_task_timestamps = [] try: # 1. 导航与页面加载判断 print(">> 正在进入【车辆点到】界面...") page.locator("span.ant-pro-menu-item-title:has-text('派件管理')").click() page.locator("div.ant-pro-menu-item:has-text('车辆点到')").click() page.get_by_role("button", name="点到").wait_for(state="visible") page.get_by_role("button", name="打印交接单").wait_for(state="visible") page.get_by_role("button", name="强卸").wait_for(state="visible") print("✅ 车辆点到界面加载完毕") # 2. 筛选条件:1天、状态=已发 -> 已到、查询 print(">> 正在设置筛选条件: 选择【1天】...") page.get_by_role("radio", name="1天").check() print(">> 正在展开【状态】下拉菜单...") page.locator( ".ant-select-selection-item", has_text=re.compile(r"已发|已到") ).click() print(">> 正在选择状态为【已到】...") page.locator(".ant-select-item-option", has_text="已到").click() print(">> 正在点击【查询】按钮...") page.get_by_role("button", name="search 查询").click() page.wait_for_timeout(2000) # 3. 获取所有【运单列表】按钮并循环处理 waybill_btns = page.get_by_role("button", name="运单列表") count = waybill_btns.count() print(f">> 共发现 {count} 个班次需要导出。") for i in range(count): print(f" ⏳ 正在处理第 {i+1}/{count} 个班次...") waybill_btns.nth(i).click() page.locator("label[title='运单查询']").wait_for(state="visible") # 4. 执行导出流程 page.get_by_role("button", name="export 导出").click() page.locator( "span.ant-transfer-list-header-title:has-text('待选导出列')" ).wait_for(state="visible") page.locator(".ant-transfer-list").first.locator( ".ant-transfer-list-header label" ).click() page.get_by_label("导出").get_by_role("button", name="right").click() page.get_by_role("button", name="export 导出数据").click() export_times.append(datetime.now()) page.locator("text=任务添加成功!").wait_for(state="visible") page.get_by_role("button", name="知道了").click() page.get_by_role("tab", name="车辆点到").click() page.wait_for_timeout(500) if count > 0: print("✅ 所有班次的导出任务已成功提交!") else: print("⚠️ 未发现任何运单列表,直接跳转至下载环节。") # 5. 关闭【运单列表】与【车辆点到】标签页(这两个页面的工作均已完成) # 运单列表为单一复用标签,所有班次导出完成后统一关闭即可。 _close_tab(page, "运单列表") _close_tab(page, "车辆点到") # 6. 前往数据导出页面去下载 print(">> 正在前往【数据导出】界面...") page.locator("a[href='/dataExport']").click() page.get_by_role("columnheader", name="任务标题").wait_for(state="visible") page.wait_for_timeout(2000) page.get_by_role("button", name="search 查询").click() page.wait_for_timeout(2000) # 7. 轮询任务状态 print(">> 列表中已渲染,开始匹配并检查后端处理状态...") while True: rows = page.locator(".ant-table-tbody > tr.ant-table-row") row_count = rows.count() pending_tasks = 0 current_ready_timestamps = [] for i in range(row_count): tds = rows.nth(i).locator("td") if tds.count() < 9: continue submit_time_str = tds.nth(2).inner_text().strip() title_str = tds.nth(5).inner_text().strip() status_str = tds.nth(6).inner_text().strip() if title_str == "发车管理运单列表": try: row_time = datetime.strptime( submit_time_str, "%Y-%m-%d %H:%M:%S" ) matched = any( abs((row_time - et).total_seconds()) <= 60 for et in export_times ) if matched: if status_str != "执行完成": pending_tasks += 1 if submit_time_str not in current_ready_timestamps: print( f" ⏳ 任务 [{submit_time_str}] 状态为【{status_str}】,数据生成中..." ) else: if submit_time_str not in current_ready_timestamps: current_ready_timestamps.append(submit_time_str) except Exception as e: print(f" ⚠️ 解析时间时出错: {e}") if pending_tasks > 0: print( f">> 共有 {pending_tasks} 个匹配任务还在处理中,等待 5 秒后刷新..." ) page.wait_for_timeout(5000) page.get_by_role("button", name="search 查询").click() page.wait_for_timeout(2000) else: target_task_timestamps = current_ready_timestamps if len(target_task_timestamps) > 0: print(">> ✅ 所有目标任务已就绪!开始并行下载...") break # 8. 下载逻辑 downloaded_files = [] for time_str in target_task_timestamps: try: target_row = page.locator(".ant-table-tbody > tr.ant-table-row").filter( has=page.locator(f"td:nth-child(3):has-text('{time_str}')") ) print(f" 🎯 触发下载 -> 任务 [{time_str}] ...") with page.expect_download() as download_info: target_row.locator("td").nth(8).locator( "button", has_text=re.compile(r"下\s*载") ).click() download = download_info.value save_path = os.path.join(download_dir, download.suggested_filename) download.save_as(save_path) downloaded_files.append(save_path) print(f" ⬇️ 文件已落盘: downloads/{download.suggested_filename}") except Exception as e: print(f" ❌ 下载任务 [{time_str}] 失败: {e}") # 9. 合并数据 (修改了文件名前缀) if downloaded_files: print("\n>> 🧪 正在开始执行扁平数据高能合并流程...") all_data_frames = [] for file_path in downloaded_files: try: df = pd.read_excel(file_path) if not df.empty: all_data_frames.append(df) except Exception as e: pass if all_data_frames: combined_df = pd.concat(all_data_frames, ignore_index=True) final_output_path = os.path.join(download_dir, "顺心-应到货物数据.xlsx") combined_df.to_excel(final_output_path, index=False) print(f"====================================================") print(f" 🎉 恭喜!合并成功!最终输出路径: {final_output_path}") print(f"====================================================") for file_path in downloaded_files: os.remove(file_path) print("✅ 临时数据清理完毕。") # 10. 关闭【数据导出】标签页(该页面工作已完成) _close_tab(page, "数据导出") print("\n🎉 【顺心 - 应到货物数据下载】全流程测试完毕!") except Exception as e: print(f"\n❌ 任务执行过程中发生异常: {e}") def shunxin_actual_download(page): """顺心:实到货物数据下载""" print("\n▶ 开始执行【顺心 - 实到货物数据下载】任务...") download_dir = os.path.join(os.getcwd(), "downloads") if not os.path.exists(download_dir): os.makedirs(download_dir) export_times = [] target_task_timestamps = [] try: # 1. 导航与页面加载 print(">> 正在进入【卸车扫描记录】界面...") page.locator("span.ant-pro-menu-item-title:has-text('派件管理')").click() page.locator("div.ant-pro-menu-item:has-text('卸车扫描记录')").click() page.get_by_role("radio", name="1天").wait_for(state="visible") print("✅ 卸车扫描记录界面加载完毕") # 2. 筛选条件:1天、查询 print(">> 正在设置筛选条件: 选择【1天】...") page.get_by_role("radio", name="1天").check() print(">> 正在点击【查询】按钮...") page.get_by_role("button", name="search 查询").click() page.wait_for_timeout(2000) # 3. 直接发起全局导出 print(">> 正在发起导出请求...") page.get_by_role("button", name="export 导出").click() page.locator( "span.ant-transfer-list-header-title:has-text('待选导出列')" ).wait_for(state="visible") page.locator(".ant-transfer-list").first.locator( ".ant-transfer-list-header label" ).click() page.get_by_label("导出").get_by_role("button", name="right").click() page.get_by_role("button", name="export 导出数据").click() export_times.append(datetime.now()) page.locator("text=任务添加成功!").wait_for(state="visible") page.get_by_role("button", name="知道了").click() print("✅ 卸车扫描记录导出任务已成功提交!") # 4. 关闭【卸车扫描记录】标签页(该页面工作已完成) _close_tab(page, "卸车扫描记录") # 5. 前往数据导出页面去下载 print(">> 正在前往【数据导出】界面...") page.locator("a[href='/dataExport']").click() page.get_by_role("columnheader", name="任务标题").wait_for(state="visible") page.wait_for_timeout(2000) page.get_by_role("button", name="search 查询").click() page.wait_for_timeout(2000) # 6. 轮询任务状态 print(">> 列表中已渲染,开始匹配并检查后端处理状态...") while True: rows = page.locator(".ant-table-tbody > tr.ant-table-row") row_count = rows.count() pending_tasks = 0 current_ready_timestamps = [] for i in range(row_count): tds = rows.nth(i).locator("td") if tds.count() < 9: continue submit_time_str = tds.nth(2).inner_text().strip() title_str = tds.nth(5).inner_text().strip() status_str = tds.nth(6).inner_text().strip() if title_str.startswith("卸车扫描记录"): try: row_time = datetime.strptime( submit_time_str, "%Y-%m-%d %H:%M:%S" ) matched = any( abs((row_time - et).total_seconds()) <= 60 for et in export_times ) if matched: if status_str != "执行完成": pending_tasks += 1 if submit_time_str not in current_ready_timestamps: print( f" ⏳ 任务 [{submit_time_str}] 状态为【{status_str}】,数据生成中..." ) else: if submit_time_str not in current_ready_timestamps: current_ready_timestamps.append(submit_time_str) except Exception as e: print(f" ⚠️ 解析时间时出错: {e}") if pending_tasks > 0: print( f">> 共有 {pending_tasks} 个匹配任务还在处理中,等待 5 秒后刷新..." ) page.wait_for_timeout(5000) page.get_by_role("button", name="search 查询").click() page.wait_for_timeout(2000) else: target_task_timestamps = current_ready_timestamps if len(target_task_timestamps) > 0: print(">> ✅ 目标任务已就绪!开始下载...") break # 7. 下载逻辑 downloaded_files = [] for time_str in target_task_timestamps: try: target_row = page.locator(".ant-table-tbody > tr.ant-table-row").filter( has=page.locator(f"td:nth-child(3):has-text('{time_str}')") ) print(f" 🎯 触发下载 -> 任务 [{time_str}] ...") with page.expect_download() as download_info: target_row.locator("td").nth(8).locator( "button", has_text=re.compile(r"下\s*载") ).click() download = download_info.value save_path = os.path.join(download_dir, download.suggested_filename) download.save_as(save_path) downloaded_files.append(save_path) print(f" ⬇️ 文件已落盘: downloads/{download.suggested_filename}") except Exception as e: print(f" ❌ 下载任务 [{time_str}] 失败: {e}") # 8. 合并数据 (修改了文件名前缀) if downloaded_files: print("\n>> 🧪 正在开始执行数据归档整理...") all_data_frames = [] for file_path in downloaded_files: try: df = pd.read_excel(file_path) if not df.empty: all_data_frames.append(df) except Exception as e: pass if all_data_frames: combined_df = pd.concat(all_data_frames, ignore_index=True) final_output_path = os.path.join(download_dir, "顺心-实到货物数据.xlsx") combined_df.to_excel(final_output_path, index=False) print(f"====================================================") print(f" 🎉 恭喜!处理成功!最终输出路径: {final_output_path}") print(f"====================================================") for file_path in downloaded_files: os.remove(file_path) print("✅ 临时数据清理完毕。") # 9. 关闭【数据导出】标签页(该页面工作已完成) _close_tab(page, "数据导出") print("\n🎉 【顺心 - 实到货物数据下载】全流程测试完毕!") except Exception as e: print(f"\n❌ 任务执行过程中发生异常: {e}")