BOM发料对比
This commit is contained in:
@@ -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"):
|
||||
|
||||
Reference in New Issue
Block a user