统一各站成败反馈信号;修复中通报错卡死与提示框误判
成败信号统一化(顺心/中通/韵达/安能/百世): - 各站 impl 开头清理上次的最终文件,避免无数据/失败时残留旧数据误导比对 - 轮询下载段加汇总校验:下载数 < 预期则 raise,堵住"零/部分下载被判成功" - 正常完成统一显式 return True(原中通/韵达/安能靠 None 隐式成功) - 无数据统一不落文件(安能实到不再落空 xlsx);韵达裸 except 改 except Exception - 中通"票数0无空提示"分支改判失败 中通 bug 修复(site_zto.py): - poll 加 stall 快速失败(连续4轮无进展即 raise)+ 刷新短超时 + 双失败 raise, 避免页面被遮挡时干等 5 分钟才触发重试 - "生成离线导出任务成功"提示所在 iframe 提交后被销毁,wait_for 抛 "Frame was detached" 属正常(提示已随 iframe 消失=任务已建立),改判成功 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
87
site_zto.py
87
site_zto.py
@@ -48,6 +48,15 @@ def zto_reset(page):
|
||||
page.wait_for_timeout(1500)
|
||||
|
||||
|
||||
def _remove_if_exists(path):
|
||||
"""删除文件(若存在):流程开头清理上次的最终文件,避免无数据/失败时残留旧数据。"""
|
||||
try:
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _wait_and_get_frame(page, text_indicator, timeout_ms=20000):
|
||||
"""在主页面与所有 iframe 中查找包含指定文本的窗口"""
|
||||
start_time = datetime.now()
|
||||
@@ -116,6 +125,9 @@ def zto_expected_download_impl(page):
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
# 清理上次的最终文件,避免本次无数据/失败时残留旧数据误导比对
|
||||
_remove_if_exists(os.path.join(download_dir, "中通-应到货物数据.xlsx"))
|
||||
|
||||
export_times = []
|
||||
|
||||
try:
|
||||
@@ -213,14 +225,14 @@ def zto_expected_download_impl(page):
|
||||
pass
|
||||
return
|
||||
else:
|
||||
print(" ⚠️ 票数为 0 但未出现空记录提示,终止流程。")
|
||||
print(" ⚠️ 票数为 0 但未出现空记录提示,页面状态异常,判失败。")
|
||||
try:
|
||||
page.locator(".mini-tab", has_text="进站交接单查询").locator(
|
||||
".mini-tab-close"
|
||||
).click()
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
raise RuntimeError("票数为 0 但未出现空记录提示,页面状态异常")
|
||||
else:
|
||||
print(" >> 票数校验通过,等待主表格渲染数据行...")
|
||||
ewb_frame.locator(
|
||||
@@ -272,16 +284,22 @@ def zto_expected_download_impl(page):
|
||||
print(" ✅ 已确认【温馨提示】弹窗。")
|
||||
|
||||
print(" >> 正在等待服务器建立后台离线任务...")
|
||||
# 提示「生成离线导出任务成功」出现在导出列 iframe(/comm/download) 中;
|
||||
# 提交完成后该 iframe 会被站点销毁,此时 wait_for 会抛 "Frame was detached"
|
||||
# ——这恰恰说明提示已随 iframe 消失、任务已建立,属正常,不视为失败。
|
||||
ctx_tips = _wait_and_get_frame(
|
||||
page, "生成离线导出任务成功", timeout_ms=10000
|
||||
)
|
||||
try:
|
||||
ctx_tips = _wait_and_get_frame(
|
||||
page, "生成离线导出任务成功", timeout_ms=10000
|
||||
)
|
||||
ctx_tips.locator(".mini-tips-success").wait_for(
|
||||
state="hidden", timeout=15000
|
||||
)
|
||||
print(" ✅ 成功提示框已消失。")
|
||||
except Exception:
|
||||
print(" ✅ 成功提示框已关闭。")
|
||||
except Exception as e:
|
||||
if "detached" in str(e).lower():
|
||||
print(" ℹ️ 提示框所在 iframe 已随提交关闭,任务已建立。")
|
||||
else:
|
||||
raise
|
||||
print(" ✅ 成功提示框已消失。")
|
||||
|
||||
export_times.append(datetime.now())
|
||||
|
||||
@@ -309,6 +327,7 @@ def zto_expected_download_impl(page):
|
||||
download_dir,
|
||||
"中通-应到货物数据.xlsx",
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ 任务执行过程中发生异常: {e}")
|
||||
@@ -334,6 +353,9 @@ def zto_actual_download_impl(page):
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
# 清理上次的最终文件,避免本次无数据/失败时残留旧数据误导比对
|
||||
_remove_if_exists(os.path.join(download_dir, "中通-实到货物数据.xlsx"))
|
||||
|
||||
export_times = []
|
||||
|
||||
try:
|
||||
@@ -444,16 +466,20 @@ def zto_actual_download_impl(page):
|
||||
print(" ✅ 已确认【温馨提示】弹窗。")
|
||||
|
||||
print(" >> 正在等待服务器建立后台离线任务...")
|
||||
# 提示「生成离线导出任务成功」出现在导出列 iframe(/comm/download) 中;
|
||||
# 提交完成后该 iframe 会被站点销毁,此时 wait_for 会抛 "Frame was detached"
|
||||
# ——这恰恰说明提示已随 iframe 消失、任务已建立,属正常,不视为失败。
|
||||
ctx_tips = _wait_and_get_frame(page, "生成离线导出任务成功", timeout_ms=10000)
|
||||
try:
|
||||
ctx_tips = _wait_and_get_frame(
|
||||
page, "生成离线导出任务成功", timeout_ms=10000
|
||||
)
|
||||
ctx_tips.locator(".mini-tips-success").wait_for(
|
||||
state="hidden", timeout=15000
|
||||
)
|
||||
print(" ✅ 成功提示框已消失。")
|
||||
except Exception:
|
||||
print(" ✅ 成功提示框已关闭。")
|
||||
except Exception as e:
|
||||
if "detached" in str(e).lower():
|
||||
print(" ℹ️ 提示框所在 iframe 已随提交关闭,任务已建立。")
|
||||
else:
|
||||
raise
|
||||
print(" ✅ 成功提示框已消失。")
|
||||
|
||||
export_times.append(datetime.now())
|
||||
|
||||
@@ -477,6 +503,7 @@ def zto_actual_download_impl(page):
|
||||
download_dir,
|
||||
"中通-实到货物数据.xlsx",
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ 任务执行过程中发生异常: {e}")
|
||||
@@ -500,6 +527,8 @@ def _zto_poll_and_download_tasks(page, export_times, download_dir, final_filenam
|
||||
poll_deadline = (
|
||||
time.monotonic() + 300
|
||||
) # 5 分钟上限:任务卡死/匹配不上时超时失败,交由上层重置重试
|
||||
last_total_found = -1
|
||||
stall_rounds = 0 # 连续无进展轮数:刷新后 total_found 不增长则累计,超阈值快速失败
|
||||
while True:
|
||||
if time.monotonic() > poll_deadline:
|
||||
raise RuntimeError(
|
||||
@@ -543,14 +572,31 @@ def _zto_poll_and_download_tasks(page, export_times, download_dir, final_filenam
|
||||
)
|
||||
|
||||
if total_found < total_expected or len(processing_timestamps) > 0:
|
||||
# 连续无进展即快速失败:避免页面被遮挡/任务卡住时干等到 5 分钟超时
|
||||
if total_found == last_total_found:
|
||||
stall_rounds += 1
|
||||
else:
|
||||
stall_rounds = 0
|
||||
last_total_found = total_found
|
||||
if stall_rounds >= 4:
|
||||
raise RuntimeError(
|
||||
f"连续 {stall_rounds} 轮刷新无进展(仍 {total_found}/{total_expected}),"
|
||||
"疑似页面被遮挡或任务异常,触发重试"
|
||||
)
|
||||
print(" ⏳ 任务尚未齐全或仍在生成,点击查询刷新...")
|
||||
# 查询按钮加短超时;失败则菜单刷新,两者都失败直接报错触发重试
|
||||
try:
|
||||
taskdone_frame.locator(
|
||||
".mini-button-text", has_text="查询"
|
||||
).first.click()
|
||||
).first.click(timeout=8000)
|
||||
except Exception as e:
|
||||
print(f" ⚠️ 查询按钮不可用,改用菜单刷新: {e}")
|
||||
page.locator("li.leaf span.menu-name", has_text="导出任务管理").click()
|
||||
print(f" ⚠️ 查询按钮不可用({e}),改用菜单刷新...")
|
||||
try:
|
||||
page.locator(
|
||||
"li.leaf span.menu-name", has_text="导出任务管理"
|
||||
).click(timeout=8000)
|
||||
except Exception as e2:
|
||||
raise RuntimeError(f"查询与菜单刷新均失败,疑似页面被遮挡: {e2}")
|
||||
|
||||
page.wait_for_timeout(3000)
|
||||
else:
|
||||
@@ -598,6 +644,13 @@ def _zto_poll_and_download_tasks(page, export_times, download_dir, final_filenam
|
||||
except Exception as e:
|
||||
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
|
||||
|
||||
# 汇总校验:下载成功数必须等于本批提交的任务数,否则判失败
|
||||
# (防"部分/全失败却被判成功")
|
||||
if len(downloaded_files) < total_expected:
|
||||
raise RuntimeError(
|
||||
f"仅成功下载 {len(downloaded_files)}/{total_expected} 个任务,数据不完整"
|
||||
)
|
||||
|
||||
# ====================================================================
|
||||
# 所有目标文件下载完成后,关闭“导出任务管理”标签页
|
||||
# ====================================================================
|
||||
|
||||
Reference in New Issue
Block a user