Harden ZTO export flows and close site tabs after each stage
- Poll #inEwbCount until it updates before reading the result, then branch on zero-count (confirm empty-data area vs missing flag) - Wait for real grid rows to render in the expected-arrival flow - Close the active site tab (进站交接单查询 / 到件扫描监控 / 导出任务管理) after each stage completes to reset the page state - Tighten export-task time-match window from 60s to 20s - Trim verbose log messages and stale comments Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
129
site_zto.py
129
site_zto.py
@@ -105,6 +105,7 @@ def zto_expected_download(page):
|
|||||||
|
|
||||||
# 3. 触发查询与深度状态机判定
|
# 3. 触发查询与深度状态机判定
|
||||||
print(">> 正在点击【查询】按钮并等待数据响应...")
|
print(">> 正在点击【查询】按钮并等待数据响应...")
|
||||||
|
old_count = ewb_frame.locator("#inEwbCount").inner_text().strip()
|
||||||
ewb_frame.locator("#searchbtn").click()
|
ewb_frame.locator("#searchbtn").click()
|
||||||
|
|
||||||
page.wait_for_timeout(1000)
|
page.wait_for_timeout(1000)
|
||||||
@@ -116,13 +117,52 @@ def zto_expected_download(page):
|
|||||||
|
|
||||||
page.wait_for_timeout(1000)
|
page.wait_for_timeout(1000)
|
||||||
|
|
||||||
empty_flag = ewb_frame.locator("span", has_text="没有搜索到符合条件的数据记录")
|
# 轮询判定统计面板更新
|
||||||
if empty_flag.is_visible():
|
wait_cycles = 0
|
||||||
print(" ⚠️ 当前查询范围内【没有搜索到符合条件的数据记录】,终止导出。")
|
while wait_cycles < 20:
|
||||||
return
|
current_count = ewb_frame.locator("#inEwbCount").inner_text().strip()
|
||||||
|
if current_count != old_count:
|
||||||
|
break
|
||||||
|
page.wait_for_timeout(500)
|
||||||
|
wait_cycles += 1
|
||||||
|
|
||||||
ticket_count_str = ewb_frame.locator("#inEwbCount").inner_text().strip()
|
ticket_count_str = ewb_frame.locator("#inEwbCount").inner_text().strip()
|
||||||
print(f" ✅ 数据渲染完毕!当前进站实际票数为: [{ticket_count_str}]")
|
print(f" ✅ 数据面板就绪!当前进站实际票数为: [{ticket_count_str}]")
|
||||||
|
|
||||||
|
# 数据分流
|
||||||
|
if (
|
||||||
|
ticket_count_str == "0"
|
||||||
|
or not ticket_count_str.isdigit()
|
||||||
|
or int(ticket_count_str) == 0
|
||||||
|
):
|
||||||
|
print(" >> 票数为 0,正在验证是否为确切的空数据区...")
|
||||||
|
empty_flag = ewb_frame.locator("#datagrid1").get_by_text(
|
||||||
|
"没有搜索到符合条件的数据记录"
|
||||||
|
)
|
||||||
|
if empty_flag.is_visible():
|
||||||
|
print(" ⚠️ 确认为空数据区,正在关闭当前标签页清理环境...")
|
||||||
|
# 即使为空数据,我们也清理关闭当前已经打开的 Tab
|
||||||
|
try:
|
||||||
|
page.locator(".mini-tab", has_text="进站交接单查询").locator(
|
||||||
|
".mini-tab-close"
|
||||||
|
).click()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print(" ⚠️ 票数为 0 但未出现空记录提示,终止流程。")
|
||||||
|
try:
|
||||||
|
page.locator(".mini-tab", has_text="进站交接单查询").locator(
|
||||||
|
".mini-tab-close"
|
||||||
|
).click()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print(" >> 票数验证通过,等待主数据表格渲染实体行...")
|
||||||
|
ewb_frame.locator(
|
||||||
|
"#datagrid1 .mini-grid-rows-view .mini-grid-row"
|
||||||
|
).first.wait_for(state="visible", timeout=15000)
|
||||||
|
|
||||||
# 4. 提取主表记录并循环双击
|
# 4. 提取主表记录并循环双击
|
||||||
main_rows = ewb_frame.locator("#datagrid1 .mini-grid-rows-view .mini-grid-row")
|
main_rows = ewb_frame.locator("#datagrid1 .mini-grid-rows-view .mini-grid-row")
|
||||||
@@ -135,7 +175,6 @@ def zto_expected_download(page):
|
|||||||
"#datagrid1 .mini-grid-rows-view .mini-grid-row"
|
"#datagrid1 .mini-grid-rows-view .mini-grid-row"
|
||||||
).nth(i)
|
).nth(i)
|
||||||
|
|
||||||
# 正则表达式清洗交接单号
|
|
||||||
raw_text = row.locator("td").nth(3).inner_text()
|
raw_text = row.locator("td").nth(3).inner_text()
|
||||||
match = re.search(r"\d{18}", raw_text)
|
match = re.search(r"\d{18}", raw_text)
|
||||||
handover_no = match.group(0) if match else raw_text.strip()
|
handover_no = match.group(0) if match else raw_text.strip()
|
||||||
@@ -164,7 +203,6 @@ def zto_expected_download(page):
|
|||||||
|
|
||||||
print(" >> [雷达扫描] 正在跨域动态追踪【温馨提示】所在的隐身视窗...")
|
print(" >> [雷达扫描] 正在跨域动态追踪【温馨提示】所在的隐身视窗...")
|
||||||
ctx_alert = _wait_and_get_frame(page, "温馨提示")
|
ctx_alert = _wait_and_get_frame(page, "温馨提示")
|
||||||
|
|
||||||
ctx_alert.locator(
|
ctx_alert.locator(
|
||||||
".mini-messagebox-buttons .mini-button-text", has_text="确定"
|
".mini-messagebox-buttons .mini-button-text", has_text="确定"
|
||||||
).click()
|
).click()
|
||||||
@@ -180,7 +218,7 @@ def zto_expected_download(page):
|
|||||||
)
|
)
|
||||||
print(" ✅ 成功提示框已平滑隐藏。")
|
print(" ✅ 成功提示框已平滑隐藏。")
|
||||||
except Exception:
|
except Exception:
|
||||||
print(" ✅ 成功提示框及其容器已自动销毁 (触发 Detached 拦截)。")
|
print(" ✅ 成功提示框及其容器已自动销毁。")
|
||||||
|
|
||||||
export_times.append(datetime.now())
|
export_times.append(datetime.now())
|
||||||
|
|
||||||
@@ -188,6 +226,19 @@ def zto_expected_download(page):
|
|||||||
ewb_frame.locator("#ewbsListNo").click()
|
ewb_frame.locator("#ewbsListNo").click()
|
||||||
page.wait_for_timeout(1000)
|
page.wait_for_timeout(1000)
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 🛡️ 优化优化:完成交接单主表的所有穿透提交后,立刻销毁本 Tab 净化内存
|
||||||
|
# ====================================================================
|
||||||
|
print(">> 📥 【进站交接单查询】任务已就绪,正在精准执行 Tab 窗口销毁复位...")
|
||||||
|
try:
|
||||||
|
page.locator(".mini-tab", has_text="进站交接单查询").locator(
|
||||||
|
".mini-tab-close"
|
||||||
|
).click()
|
||||||
|
page.wait_for_timeout(500)
|
||||||
|
print(" ✅ 【进站交接单查询】Tab 已成功关闭。")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ 尝试关闭【进站交接单查询】Tab 时遇到轻微阻碍: {e}")
|
||||||
|
|
||||||
# 核心逻辑交由统一的轮询下载引擎处理
|
# 核心逻辑交由统一的轮询下载引擎处理
|
||||||
_zto_poll_and_download_tasks(
|
_zto_poll_and_download_tasks(
|
||||||
page,
|
page,
|
||||||
@@ -259,7 +310,7 @@ def zto_actual_download(page):
|
|||||||
|
|
||||||
page.wait_for_timeout(500)
|
page.wait_for_timeout(500)
|
||||||
|
|
||||||
# 3. 设定单号类型(树形下拉框选择子单)
|
# 3. 设定单号类型
|
||||||
print(">> 正在设定单号类型为【子单】...")
|
print(">> 正在设定单号类型为【子单】...")
|
||||||
arr_frame.locator('[id="bandEwbType$text"]').click()
|
arr_frame.locator('[id="bandEwbType$text"]').click()
|
||||||
page.wait_for_timeout(500)
|
page.wait_for_timeout(500)
|
||||||
@@ -282,9 +333,19 @@ def zto_actual_download(page):
|
|||||||
|
|
||||||
page.wait_for_timeout(1000)
|
page.wait_for_timeout(1000)
|
||||||
|
|
||||||
empty_flag = arr_frame.locator("span", has_text="没有搜索到符合条件的数据记录")
|
empty_flag = arr_frame.locator("#datagrid1").get_by_text(
|
||||||
|
"没有搜索到符合条件的数据记录"
|
||||||
|
)
|
||||||
if empty_flag.is_visible():
|
if empty_flag.is_visible():
|
||||||
print(" ⚠️ 当前查询范围内【没有搜索到符合条件的数据记录】,终止导出。")
|
print(
|
||||||
|
" ⚠️ 当前查询范围内【没有搜索到符合条件的数据记录】,终止并关闭环境。"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
page.locator(".mini-tab", has_text="到件扫描监控").locator(
|
||||||
|
".mini-tab-close"
|
||||||
|
).click()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
return
|
return
|
||||||
|
|
||||||
arr_frame.locator("#page1").wait_for(state="visible", timeout=15000)
|
arr_frame.locator("#page1").wait_for(state="visible", timeout=15000)
|
||||||
@@ -304,7 +365,6 @@ def zto_actual_download(page):
|
|||||||
|
|
||||||
print(" >> [雷达扫描] 正在跨域动态追踪【温馨提示】所在的隐身视窗...")
|
print(" >> [雷达扫描] 正在跨域动态追踪【温馨提示】所在的隐身视窗...")
|
||||||
ctx_alert = _wait_and_get_frame(page, "温馨提示")
|
ctx_alert = _wait_and_get_frame(page, "温馨提示")
|
||||||
|
|
||||||
ctx_alert.locator(
|
ctx_alert.locator(
|
||||||
".mini-messagebox-buttons .mini-button-text", has_text="确定"
|
".mini-messagebox-buttons .mini-button-text", has_text="确定"
|
||||||
).click()
|
).click()
|
||||||
@@ -320,10 +380,23 @@ def zto_actual_download(page):
|
|||||||
)
|
)
|
||||||
print(" ✅ 成功提示框已平滑隐藏。")
|
print(" ✅ 成功提示框已平滑隐藏。")
|
||||||
except Exception:
|
except Exception:
|
||||||
print(" ✅ 成功提示框及其容器已自动销毁 (触发 Detached 拦截)。")
|
print(" ✅ 成功提示框及其容器已自动销毁。")
|
||||||
|
|
||||||
export_times.append(datetime.now())
|
export_times.append(datetime.now())
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 🛡️ 优化优化:完成实到主表的数据导出提交后,立刻销毁本 Tab 恢复空态
|
||||||
|
# ====================================================================
|
||||||
|
print(">> 📥 【到件扫描监控】任务已就绪,正在精准执行 Tab 窗口销毁复位...")
|
||||||
|
try:
|
||||||
|
page.locator(".mini-tab", has_text="到件扫描监控").locator(
|
||||||
|
".mini-tab-close"
|
||||||
|
).click()
|
||||||
|
page.wait_for_timeout(500)
|
||||||
|
print(" ✅ 【到件扫描监控】Tab 已成功关闭。")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ 尝试关闭【到件扫描监控】Tab 时遇到轻微阻碍: {e}")
|
||||||
|
|
||||||
# 核心逻辑交由统一的轮询下载引擎处理
|
# 核心逻辑交由统一的轮询下载引擎处理
|
||||||
_zto_poll_and_download_tasks(
|
_zto_poll_and_download_tasks(
|
||||||
page,
|
page,
|
||||||
@@ -337,9 +410,6 @@ def zto_actual_download(page):
|
|||||||
print(f"\n❌ 任务执行过程中发生异常: {e}")
|
print(f"\n❌ 任务执行过程中发生异常: {e}")
|
||||||
|
|
||||||
|
|
||||||
# ====================================================================
|
|
||||||
# 🛡️ 优化核心:多网点通用任务匹配、界面重载刷新、并行状态机引擎
|
|
||||||
# ====================================================================
|
|
||||||
def _zto_poll_and_download_tasks(
|
def _zto_poll_and_download_tasks(
|
||||||
page, export_times, target_task_title, download_dir, final_filename
|
page, export_times, target_task_title, download_dir, final_filename
|
||||||
):
|
):
|
||||||
@@ -353,9 +423,7 @@ def _zto_poll_and_download_tasks(
|
|||||||
)
|
)
|
||||||
page.wait_for_timeout(1000)
|
page.wait_for_timeout(1000)
|
||||||
|
|
||||||
print(
|
print(">> 列表中已渲染,开始匹配并检查后端处理状态...")
|
||||||
">> 列表中已渲染,开始执行【1分钟误差+标题判定+缺单自动点击查询刷新】高级断言..."
|
|
||||||
)
|
|
||||||
total_expected = len(export_times)
|
total_expected = len(export_times)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@@ -376,13 +444,11 @@ def _zto_poll_and_download_tasks(
|
|||||||
submit_time_str = tds.nth(4).inner_text().strip()
|
submit_time_str = tds.nth(4).inner_text().strip()
|
||||||
status_str = tds.nth(6).inner_text().strip()
|
status_str = tds.nth(6).inner_text().strip()
|
||||||
|
|
||||||
# 严格核对任务标题
|
|
||||||
if title_str == target_task_title:
|
if title_str == target_task_title:
|
||||||
try:
|
try:
|
||||||
row_time = datetime.strptime(submit_time_str, "%Y-%m-%d %H:%M:%S")
|
row_time = datetime.strptime(submit_time_str, "%Y-%m-%d %H:%M:%S")
|
||||||
# 【核心优化】:核实当前行是否落在任何一个发起的任务的 1 分钟之内 (<= 60秒)
|
|
||||||
matched = any(
|
matched = any(
|
||||||
abs((row_time - et).total_seconds()) <= 60
|
abs((row_time - et).total_seconds()) <= 20
|
||||||
for et in export_times
|
for et in export_times
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -394,17 +460,14 @@ def _zto_poll_and_download_tasks(
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 状态研判
|
|
||||||
total_found = len(ready_timestamps) + len(processing_timestamps)
|
total_found = len(ready_timestamps) + len(processing_timestamps)
|
||||||
print(
|
print(
|
||||||
f" 📊 状态盘点:期望任务数 [{total_expected}],实际入表 [{total_found}] (就绪 [{len(ready_timestamps)}],处理中 [{len(processing_timestamps)}])"
|
f" 📊 状态盘点:期望任务数 [{total_expected}],实际入表 [{total_found}] (就绪 [{len(ready_timestamps)}],处理中 [{len(processing_timestamps)}])"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 【核心分支】:如果列表中出现的数量还不够,或者有任务在“执行中”,说明数据还没完全落盘
|
|
||||||
if total_found < total_expected or len(processing_timestamps) > 0:
|
if total_found < total_expected or len(processing_timestamps) > 0:
|
||||||
print(" ⏳ 发现任务缺失或正在生成,执行【点击查询按钮】触发局部重载...")
|
print(" ⏳ 发现任务缺失或正在生成,执行【点击查询按钮】触发局部重载...")
|
||||||
try:
|
try:
|
||||||
# 寻找并在 iframe 内部执行局部刷新按钮的点击
|
|
||||||
taskdone_frame.locator(
|
taskdone_frame.locator(
|
||||||
".mini-button-text", has_text="查询"
|
".mini-button-text", has_text="查询"
|
||||||
).first.click()
|
).first.click()
|
||||||
@@ -412,9 +475,8 @@ def _zto_poll_and_download_tasks(
|
|||||||
print(f" ⚠️ 局部刷新按钮失效,使用菜单后备刷新: {e}")
|
print(f" ⚠️ 局部刷新按钮失效,使用菜单后备刷新: {e}")
|
||||||
page.locator("li.leaf span.menu-name", has_text="导出任务管理").click()
|
page.locator("li.leaf span.menu-name", has_text="导出任务管理").click()
|
||||||
|
|
||||||
page.wait_for_timeout(3000) # 挂起 3 秒进入下一轮状态机检测
|
page.wait_for_timeout(3000)
|
||||||
else:
|
else:
|
||||||
# 数量全满,且全部为“成功执行”
|
|
||||||
target_task_timestamps = list(ready_timestamps)
|
target_task_timestamps = list(ready_timestamps)
|
||||||
print(">> ✅ 目标任务已全部生成,解除锁定,开始接收文件流...")
|
print(">> ✅ 目标任务已全部生成,解除锁定,开始接收文件流...")
|
||||||
break
|
break
|
||||||
@@ -459,6 +521,19 @@ def _zto_poll_and_download_tasks(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
|
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 🛡️ 优化优化:所有目标任务文件全部下载完毕后,立刻销毁“导出任务管理” Tab
|
||||||
|
# ====================================================================
|
||||||
|
print(">> 📥 【导出任务管理】所有数据拉取流闭环,正在执行 Tab 窗口销毁复位...")
|
||||||
|
try:
|
||||||
|
page.locator(".mini-tab", has_text="导出任务管理").locator(
|
||||||
|
".mini-tab-close"
|
||||||
|
).click()
|
||||||
|
page.wait_for_timeout(500)
|
||||||
|
print(" ✅ 【导出任务管理】Tab 已安全关闭。页面归档空态。")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ 尝试收尾关闭【导出任务管理】Tab 时有波动: {e}")
|
||||||
|
|
||||||
# 9. 合并数据
|
# 9. 合并数据
|
||||||
if downloaded_files:
|
if downloaded_files:
|
||||||
print("\n>> 🧪 正在开始执行扁平数据高能合并与清洗流程...")
|
print("\n>> 🧪 正在开始执行扁平数据高能合并与清洗流程...")
|
||||||
|
|||||||
Reference in New Issue
Block a user