Centralize paths in paths.py and harden waybill-number handling
- Add paths.py: BASE_DIR/DOWNLOAD_DIR/CONFIG_PATH anchored on __file__ so paths resolve regardless of the launch cwd - Route main_router and all site modules through DOWNLOAD_DIR/CONFIG_PATH, replacing os.getcwd()-based download dirs and the "config.yaml" literal - main_router compare engine: read Excel as str with keep_default_na, strip/normalize 运单号, drop blanks, and isin against a set so int/float-vs-str mismatches no longer produce false "undelivered" - Shunxin: give downloaded files unique microsecond temp names and read Excel as str to preserve long-waybill precision - Normalize bare except: to except Exception: across affected files Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ import yaml
|
||||
import pandas as pd
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
from paths import DOWNLOAD_DIR, CONFIG_PATH
|
||||
|
||||
# 导入抽离出去的各个网点模块
|
||||
import site_shunxin
|
||||
import site_baishi
|
||||
@@ -34,7 +36,7 @@ def task_process_undelivered_data(site_name="顺心"):
|
||||
"""全局模块:应到未到异常件比对引擎 (支持动态网点前缀)"""
|
||||
print(f"\n▶ 开始执行【{site_name} - 应到未到数据处理】任务...")
|
||||
|
||||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||||
download_dir = DOWNLOAD_DIR
|
||||
expected_path = os.path.join(download_dir, f"{site_name}-应到货物数据.xlsx")
|
||||
actual_path = os.path.join(download_dir, f"{site_name}-实到货物数据.xlsx")
|
||||
output_path = os.path.join(download_dir, f"{site_name}-应到未到货物数据.xlsx")
|
||||
@@ -49,8 +51,9 @@ def task_process_undelivered_data(site_name="顺心"):
|
||||
|
||||
try:
|
||||
print(">> 正在载入本地 Excel 文档...")
|
||||
df_expected = pd.read_excel(expected_path)
|
||||
df_actual = pd.read_excel(actual_path)
|
||||
# 全部按字符串读取并保留空串:避免 18 位运单号被当成浮点数丢精度,也避免空单元格变成 NaN。
|
||||
df_expected = pd.read_excel(expected_path, dtype=str, keep_default_na=False)
|
||||
df_actual = pd.read_excel(actual_path, dtype=str, keep_default_na=False)
|
||||
|
||||
if "运单号" not in df_expected.columns or "运单号" not in df_actual.columns:
|
||||
print("❌ 核心资产校验失败:数据源中缺失【运单号】字段,请检查导出配置。")
|
||||
@@ -58,8 +61,16 @@ def task_process_undelivered_data(site_name="顺心"):
|
||||
|
||||
print(">> 正在启动多维数据集比对引擎...")
|
||||
|
||||
# 统一运单号为去空白字符串,消除 int/float 与 str 混读导致 isin 永不命中的隐患
|
||||
df_expected["运单号"] = df_expected["运单号"].astype(str).str.strip()
|
||||
df_actual["运单号"] = df_actual["运单号"].astype(str).str.strip()
|
||||
|
||||
# 剔除空白运单号,避免空值被误判为“应到未到”
|
||||
df_expected = df_expected[df_expected["运单号"] != ""]
|
||||
actual_set = set(df_actual["运单号"]) - {""}
|
||||
|
||||
# Left Anti-Join:在应到中找出不存在于实到里的运单号
|
||||
df_undelivered = df_expected[~df_expected["运单号"].isin(df_actual["运单号"])]
|
||||
df_undelivered = df_expected[~df_expected["运单号"].isin(actual_set)]
|
||||
|
||||
target_columns = ["班次号", "交接单号", "运单号"]
|
||||
available_columns = [
|
||||
@@ -86,8 +97,8 @@ def run_multi_site_daemon():
|
||||
debug_mode = False
|
||||
debug_target = ""
|
||||
try:
|
||||
if os.path.exists("config.yaml"):
|
||||
with open("config.yaml", "r", encoding="utf-8") as f:
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
config = yaml.safe_load(f)
|
||||
debug_mode = config.get("debug", {}).get("enabled", False)
|
||||
debug_target = config.get("debug", {}).get("target_site", "")
|
||||
@@ -191,7 +202,7 @@ def run_multi_site_daemon():
|
||||
if read_btns.nth(i).is_visible(timeout=500):
|
||||
read_btns.nth(i).click()
|
||||
handled_any = True
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if bs_page.locator("button:has-text('关 闭')").is_visible(
|
||||
@@ -199,7 +210,7 @@ def run_multi_site_daemon():
|
||||
):
|
||||
bs_page.locator("button:has-text('关 闭')").click()
|
||||
handled_any = True
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
if not handled_any:
|
||||
break
|
||||
|
||||
15
paths.py
Normal file
15
paths.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# paths.py
|
||||
# 统一的路径锚点:所有路径都以本项目所在目录为基准,避免依赖运行时的工作目录(cwd)。
|
||||
# 这样无论从哪个目录启动脚本(IDE / 命令行 / 计划任务 / 双击),
|
||||
# 下载目录与配置文件都能稳定定位,不会出现“文件落到别处”或“读不到密码”的隐蔽故障。
|
||||
|
||||
import os
|
||||
|
||||
# 项目根目录(以本文件所在位置为基准,与从哪个目录启动脚本无关)
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# 统一的下载 / 输出目录
|
||||
DOWNLOAD_DIR = os.path.join(BASE_DIR, "downloads")
|
||||
|
||||
# 统一的配置文件路径(注意:config.yaml 需与本项目脚本放在同一目录下)
|
||||
CONFIG_PATH = os.path.join(BASE_DIR, "config.yaml")
|
||||
@@ -4,12 +4,14 @@ import os
|
||||
import yaml
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
from paths import DOWNLOAD_DIR, CONFIG_PATH
|
||||
|
||||
|
||||
def baishi_download_undelivered_data(page):
|
||||
"""百世:一键提取应到未到(当日未扫)数据"""
|
||||
print("\n▶ 开始执行【百世 - 一键提取应到未到数据】任务...")
|
||||
|
||||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||||
download_dir = DOWNLOAD_DIR
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
@@ -60,7 +62,7 @@ def baishi_download_undelivered_data(page):
|
||||
print(">> 正在读取本地配置文件并进行授权验证...")
|
||||
password = ""
|
||||
try:
|
||||
with open("config.yaml", "r", encoding="utf-8") as f:
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
config = yaml.safe_load(f)
|
||||
password = config.get("baishi", {}).get("password", "")
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import re
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
|
||||
from paths import DOWNLOAD_DIR
|
||||
|
||||
|
||||
def _close_tab(page, tab_name):
|
||||
"""关闭指定名称的标签页(Ant Design Tabs)。
|
||||
@@ -34,7 +36,7 @@ def shunxin_expected_download(page):
|
||||
print("\n▶ 开始执行【顺心 - 应到货物数据下载】任务...")
|
||||
|
||||
# 初始化并创建下载目录
|
||||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||||
download_dir = DOWNLOAD_DIR
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
print(f">> 已创建专属下载文件夹: {download_dir}")
|
||||
@@ -189,10 +191,13 @@ def shunxin_expected_download(page):
|
||||
).click()
|
||||
|
||||
download = download_info.value
|
||||
save_path = os.path.join(download_dir, download.suggested_filename)
|
||||
# 用带微秒的时间戳生成唯一临时文件名,避免同名任务相互覆盖导致丢数据
|
||||
safe_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
||||
custom_filename = f"顺心_temp_{safe_timestamp}.xlsx"
|
||||
save_path = os.path.join(download_dir, custom_filename)
|
||||
download.save_as(save_path)
|
||||
downloaded_files.append(save_path)
|
||||
print(f" ⬇️ 文件已落盘: downloads/{download.suggested_filename}")
|
||||
print(f" ⬇️ 文件已落盘: downloads/{custom_filename}")
|
||||
except Exception as e:
|
||||
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
|
||||
|
||||
@@ -202,7 +207,7 @@ def shunxin_expected_download(page):
|
||||
all_data_frames = []
|
||||
for file_path in downloaded_files:
|
||||
try:
|
||||
df = pd.read_excel(file_path)
|
||||
df = pd.read_excel(file_path, dtype=str)
|
||||
if not df.empty:
|
||||
all_data_frames.append(df)
|
||||
except Exception as e:
|
||||
@@ -233,7 +238,7 @@ def shunxin_actual_download(page):
|
||||
"""顺心:实到货物数据下载"""
|
||||
print("\n▶ 开始执行【顺心 - 实到货物数据下载】任务...")
|
||||
|
||||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||||
download_dir = DOWNLOAD_DIR
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
@@ -358,10 +363,13 @@ def shunxin_actual_download(page):
|
||||
).click()
|
||||
|
||||
download = download_info.value
|
||||
save_path = os.path.join(download_dir, download.suggested_filename)
|
||||
# 用带微秒的时间戳生成唯一临时文件名,避免同名任务相互覆盖导致丢数据
|
||||
safe_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
||||
custom_filename = f"顺心_temp_{safe_timestamp}.xlsx"
|
||||
save_path = os.path.join(download_dir, custom_filename)
|
||||
download.save_as(save_path)
|
||||
downloaded_files.append(save_path)
|
||||
print(f" ⬇️ 文件已落盘: downloads/{download.suggested_filename}")
|
||||
print(f" ⬇️ 文件已落盘: downloads/{custom_filename}")
|
||||
except Exception as e:
|
||||
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
|
||||
|
||||
@@ -371,7 +379,7 @@ def shunxin_actual_download(page):
|
||||
all_data_frames = []
|
||||
for file_path in downloaded_files:
|
||||
try:
|
||||
df = pd.read_excel(file_path)
|
||||
df = pd.read_excel(file_path, dtype=str)
|
||||
if not df.empty:
|
||||
all_data_frames.append(df)
|
||||
except Exception as e:
|
||||
|
||||
@@ -6,6 +6,8 @@ import yaml
|
||||
from datetime import datetime, timedelta
|
||||
import pandas as pd
|
||||
|
||||
from paths import DOWNLOAD_DIR, CONFIG_PATH
|
||||
|
||||
|
||||
def yunda_login(page):
|
||||
"""
|
||||
@@ -26,8 +28,8 @@ def yunda_login(page):
|
||||
# 从配置读取凭证(默认空串;真实凭据仅存于被忽略的 config.yaml)
|
||||
username = ""
|
||||
password = ""
|
||||
if os.path.exists("config.yaml"):
|
||||
with open("config.yaml", "r", encoding="utf-8") as f:
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
config = yaml.safe_load(f) or {}
|
||||
yd_cfg = config.get("yunda", {})
|
||||
username = str(yd_cfg.get("username", ""))
|
||||
@@ -91,7 +93,7 @@ def yunda_expected_download(page):
|
||||
print("\n▶ 开始执行【韵达 - 应到货物数据下载】任务...")
|
||||
target_task_title = "进站主单表"
|
||||
|
||||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||||
download_dir = DOWNLOAD_DIR
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
@@ -116,8 +118,8 @@ def yunda_expected_download(page):
|
||||
# 2. 从配置文件中解析并计算绝对日期跨度
|
||||
query_days = 1
|
||||
try:
|
||||
if os.path.exists("config.yaml"):
|
||||
with open("config.yaml", "r", encoding="utf-8") as f:
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
config = yaml.safe_load(f) or {}
|
||||
query_days = int(config.get("yunda", {}).get("query_days", 1))
|
||||
except Exception as e:
|
||||
@@ -307,7 +309,7 @@ def yunda_actual_download(page):
|
||||
print("\n▶ 开始执行【韵达 - 实到货物数据下载】任务...")
|
||||
target_task_title = "扫描记录数据"
|
||||
|
||||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||||
download_dir = DOWNLOAD_DIR
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
@@ -331,8 +333,8 @@ def yunda_actual_download(page):
|
||||
|
||||
query_days = 1
|
||||
try:
|
||||
if os.path.exists("config.yaml"):
|
||||
with open("config.yaml", "r", encoding="utf-8") as f:
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
config = yaml.safe_load(f) or {}
|
||||
query_days = int(config.get("yunda", {}).get("query_days", 1))
|
||||
except Exception:
|
||||
@@ -578,7 +580,7 @@ def _yunda_poll_and_download_tasks(
|
||||
".el-icon-close"
|
||||
).click()
|
||||
print(" ✅ 【导出服务】工作区已安全关闭。")
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if downloaded_files:
|
||||
@@ -589,7 +591,7 @@ def _yunda_poll_and_download_tasks(
|
||||
df = pd.read_excel(file_path, dtype=str)
|
||||
if not df.empty:
|
||||
all_dfs.append(df)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if all_dfs:
|
||||
|
||||
24
site_zto.py
24
site_zto.py
@@ -6,6 +6,8 @@ import yaml
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
|
||||
from paths import DOWNLOAD_DIR, CONFIG_PATH
|
||||
|
||||
|
||||
def _wait_and_get_frame(page, text_indicator, timeout_ms=20000):
|
||||
"""动态雷达探测器:全域扫描所有视窗"""
|
||||
@@ -14,14 +16,14 @@ def _wait_and_get_frame(page, text_indicator, timeout_ms=20000):
|
||||
try:
|
||||
if page.get_by_text(text_indicator).count() > 0:
|
||||
return page
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for frame in page.frames:
|
||||
try:
|
||||
if frame.get_by_text(text_indicator).count() > 0:
|
||||
return frame
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
page.wait_for_timeout(300)
|
||||
@@ -49,7 +51,7 @@ def zto_expected_download(page):
|
||||
print("\n▶ 开始执行【中通 - 应到货物数据下载】任务...")
|
||||
target_task_title = "进站交接单查询-运单信息"
|
||||
|
||||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||||
download_dir = DOWNLOAD_DIR
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
@@ -67,8 +69,8 @@ def zto_expected_download(page):
|
||||
# 读取 YAML 配置设定天数
|
||||
query_days = 1
|
||||
try:
|
||||
if os.path.exists("config.yaml"):
|
||||
with open("config.yaml", "r", encoding="utf-8") as f:
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
config = yaml.safe_load(f) or {}
|
||||
query_days = int(config.get("zto", {}).get("query_days", 1))
|
||||
except Exception as e:
|
||||
@@ -146,7 +148,7 @@ def zto_expected_download(page):
|
||||
page.locator(".mini-tab", has_text="进站交接单查询").locator(
|
||||
".mini-tab-close"
|
||||
).click()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
else:
|
||||
@@ -155,7 +157,7 @@ def zto_expected_download(page):
|
||||
page.locator(".mini-tab", has_text="进站交接单查询").locator(
|
||||
".mini-tab-close"
|
||||
).click()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
else:
|
||||
@@ -257,7 +259,7 @@ def zto_actual_download(page):
|
||||
print("\n▶ 开始执行【中通 - 实到货物数据下载】任务...")
|
||||
target_task_title = "到件扫描管理"
|
||||
|
||||
download_dir = os.path.join(os.getcwd(), "downloads")
|
||||
download_dir = DOWNLOAD_DIR
|
||||
if not os.path.exists(download_dir):
|
||||
os.makedirs(download_dir)
|
||||
|
||||
@@ -275,8 +277,8 @@ def zto_actual_download(page):
|
||||
# 2. 读取 YAML 并设定时间范围
|
||||
query_days = 1
|
||||
try:
|
||||
if os.path.exists("config.yaml"):
|
||||
with open("config.yaml", "r", encoding="utf-8") as f:
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
config = yaml.safe_load(f) or {}
|
||||
query_days = int(config.get("zto", {}).get("query_days", 1))
|
||||
except Exception:
|
||||
@@ -344,7 +346,7 @@ def zto_actual_download(page):
|
||||
page.locator(".mini-tab", has_text="到件扫描监控").locator(
|
||||
".mini-tab-close"
|
||||
).click()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user