BOM发料对比

This commit is contained in:
hjq
2026-06-23 11:02:37 +08:00
parent a8a6388d85
commit 9be2b1373f
5 changed files with 111 additions and 35 deletions

View File

@@ -3,8 +3,11 @@ ERP 登录模块 - DrissionPage
"""
import os
import sys
import time
import shutil
import datetime
import subprocess
import urllib.request
from pathlib import Path
from dotenv import load_dotenv
@@ -29,6 +32,11 @@ def is_docker_env() -> bool:
return os.path.exists("/.dockerenv")
def is_linux_env() -> bool:
"""判断当前是否运行在 Linux 环境。"""
return sys.platform.startswith("linux")
def get_docker_tmp_root() -> Path:
"""
指定 DrissionPage 在 Docker 中的临时根目录。
@@ -39,6 +47,57 @@ def get_docker_tmp_root() -> Path:
return tmp_root
def resolve_browser_path() -> str:
"""
统一解析浏览器路径。
Linux 生产环境优先使用 Google Chrome Stable避免 Chromium 与
DrissionPage 在 DevTools WebSocket 握手阶段出现兼容性问题。
"""
env_candidates = [
os.getenv("DRISSION_BROWSER_PATH", "").strip(),
os.getenv("CHROME_BIN", "").strip(),
os.getenv("BROWSER_PATH", "").strip(),
]
for candidate in env_candidates:
if candidate and os.path.exists(candidate):
return candidate
if is_linux_env():
browser_candidates = [
"/usr/bin/google-chrome",
"/usr/bin/google-chrome-stable",
"/usr/bin/chromium",
"/usr/bin/chromium-browser",
]
else:
browser_candidates = [
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
shutil.which("google-chrome") or "",
shutil.which("chromium") or "",
shutil.which("chromium-browser") or "",
]
for candidate in browser_candidates:
if candidate and os.path.exists(candidate):
return candidate
return ""
def cleanup_debug_port(address: str) -> None:
"""按实际 DevTools 端口清理僵尸浏览器进程。"""
if not address or ":" not in address:
return
debug_port = address.rsplit(":", 1)[-1]
subprocess.run(
f"lsof -ti tcp:{debug_port} | xargs -r kill -9",
shell=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
# ── 日志 ──────────────────────────────────────────────────────────────────────
def log(level: str, msg: str):
icons = {"INFO": " ", "OK": "", "WARN": "⚠️ ", "ERR": ""}
@@ -63,43 +122,40 @@ def dump_page_state(page: ChromiumPage, label: str = ""):
# ── 浏览器 ────────────────────────────────────────────────────────────────────
def get_page(headless: bool = False, port: int = 9222) -> ChromiumPage:
co = ChromiumOptions()
# 强制在生产环境下使用无头模式
is_docker = is_docker_env()
if headless or is_docker:
browser_path = resolve_browser_path()
effective_headless = headless or is_docker
if effective_headless:
co.set_argument("--headless=new")
co.set_argument("--disable-gpu") # Docker 无头模式下强烈建议禁用 GPU防止 404 和渲染崩溃
co.set_argument("--disable-gpu")
co.set_argument("--disable-blink-features=AutomationControlled")
co.set_argument("--no-sandbox")
co.set_argument("--disable-dev-shm-usage") # 防止 Docker 共享内存耗尽导致浏览器崩溃
co.set_argument("--disable-software-rasterizer") # 配合无头模式禁用软件光栅化器
co.set_argument("--remote-allow-origins=*") # 解决 Docker 下 websocket 404 问题
co.set_argument("--disable-dev-shm-usage")
co.set_argument("--disable-software-rasterizer")
co.set_argument("--remote-allow-origins=*")
co.set_argument("--remote-debugging-address=127.0.0.1")
co.set_argument("--disable-web-security")
co.set_argument("--ignore-certificate-errors")
co.set_argument("--proxy-server=direct://") # 禁用代理
co.set_argument("--proxy-server=direct://")
co.set_argument("--proxy-bypass-list=*")
co.set_argument("--window-size=1440,900")
if browser_path:
co.set_browser_path(browser_path)
log("INFO", f"选用浏览器内核: {browser_path}")
else:
log("WARN", "未解析到明确浏览器路径,将使用 DrissionPage 默认浏览器发现逻辑。")
if is_docker:
# Docker 生产环境:由 DrissionPage 自动分配独立端口和 profile避免僵尸会话导致 404
tmp_root = get_docker_tmp_root()
co.set_tmp_path(str(tmp_root))
co.auto_port(True)
log("INFO", f"Docker Drission 临时目录: {tmp_root}")
# 很多 Debian/Ubuntu 系统的 Chromium 实际上是通过 wrapper 脚本调用的
# 直接指定确切的执行路径,防止 DrissionPage 底层启动失败
if os.path.exists('/usr/bin/chromium'):
co.set_browser_path('/usr/bin/chromium')
elif os.path.exists('/usr/bin/chromium-browser'):
co.set_browser_path('/usr/bin/chromium-browser')
elif os.path.exists('/usr/bin/google-chrome'):
co.set_browser_path('/usr/bin/google-chrome')
else:
# 本地开发环境:使用固定端口,方便复用
co.set_local_port(port)
# #region debug-point A:drission-target
opt = handle_options(co)
log(
@@ -122,6 +178,18 @@ def get_page(headless: bool = False, port: int = 9222) -> ChromiumPage:
page = ChromiumPage(opt)
return page
except Exception as e:
actual_address = opt.address or f"127.0.0.1:{port}"
log("WARN", f"[DEBUG] ChromiumPage 初始化失败: {e},尝试清理地址 {actual_address} 后重试...")
try:
cleanup_debug_port(actual_address)
time.sleep(1)
page = ChromiumPage(opt)
log("OK", "[DEBUG] 清理后重试成功!")
return page
except Exception as retry_e:
log("ERR", f"[DEBUG] 清理后重试依然失败: {retry_e}")
e = retry_e
# #region debug-point B:devtools-http-probe
if opt.address:
for endpoint in ("json/version", "json/list"):