diff --git a/plugins/sehuatang_push/shehuatang_undetected.py b/plugins/sehuatang_push/shehuatang_undetected.py index c009e2d..bcabccf 100644 --- a/plugins/sehuatang_push/shehuatang_undetected.py +++ b/plugins/sehuatang_push/shehuatang_undetected.py @@ -1,5 +1,7 @@ import time import os +import shutil +import subprocess import requests from io import BytesIO import undetected_chromedriver as uc @@ -11,6 +13,7 @@ import undetected_chromedriver as uc # except Exception: # pass from selenium.webdriver.common.by import By +from selenium.common.exceptions import SessionNotCreatedException from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from bs4 import BeautifulSoup @@ -27,6 +30,87 @@ from PyPDF2 import PdfReader, PdfWriter from loguru import logger +def _detect_local_chrome_major_version(): + """检测本机 Chrome/Chromium 主版本号,尽量让 ChromeDriver 跟浏览器版本保持一致。""" + # 这里按不同平台准备一组常见的 Chrome/Chromium 可执行文件位置。 + # 这样做的目的,是避免把 driver 版本写死后,浏览器一升级就再次出现版本不兼容。 + candidate_paths = [] + if os.name == 'nt': + candidate_paths.extend([ + os.path.join(os.environ.get("PROGRAMFILES", ""), "Google", "Chrome", "Application", "chrome.exe"), + os.path.join(os.environ.get("PROGRAMFILES(X86)", ""), "Google", "Chrome", "Application", "chrome.exe"), + os.path.join(os.environ.get("LOCALAPPDATA", ""), "Google", "Chrome", "Application", "chrome.exe"), + ]) + candidate_paths.extend([ + shutil.which("chrome"), + shutil.which("chrome.exe"), + ]) + else: + candidate_paths.extend([ + shutil.which("google-chrome"), + shutil.which("google-chrome-stable"), + shutil.which("chromium"), + shutil.which("chromium-browser"), + shutil.which("chrome"), + ]) + + # 依次尝试执行 `--version`,只要拿到类似 `147.0.7727.116` 的版本串,就提取主版本号返回。 + for chrome_path in candidate_paths: + if not chrome_path or not os.path.exists(chrome_path): + continue + + try: + version_output = subprocess.check_output( + [chrome_path, "--version"], + stderr=subprocess.STDOUT, + text=True, + timeout=5, + ).strip() + version_match = re.search(r"(\d+)\.", version_output) + if version_match: + return int(version_match.group(1)) + except Exception as exc: + logger.debug(f"检测浏览器版本失败,路径={chrome_path},原因={exc}") + + return None + + +def _extract_browser_major_version_from_error(error_message): + """从 Selenium/ChromeDriver 报错中提取当前浏览器主版本号,用于兜底重试。""" + # ChromeDriver 版本不匹配时,报错里通常会带 `Current browser version is 147.x.x.x` 这样的信息。 + # 这里把这个版本号解析出来,便于在首次启动失败后自动切换到正确版本再试一次。 + version_match = re.search(r"Current browser version is (\d+)\.", error_message or "") + if version_match: + return int(version_match.group(1)) + return None + + +def _create_chrome_driver(options): + """创建 undetected_chromedriver 实例,并在版本不匹配时自动兜底重试一次。""" + detected_major_version = _detect_local_chrome_major_version() + chrome_kwargs = {"options": options} + + # 优先使用本机已安装浏览器的主版本号,避免继续使用过期的 driver 版本。 + if detected_major_version: + chrome_kwargs["version_main"] = detected_major_version + logger.info(f"检测到本机 Chrome/Chromium 主版本: {detected_major_version}") + else: + logger.warning("未检测到本机 Chrome/Chromium 版本,将交给 undetected_chromedriver 自动处理") + + try: + return uc.Chrome(**chrome_kwargs) + except SessionNotCreatedException as exc: + # 如果首次启动失败,并且报错里明确告诉了当前浏览器版本,就按真实版本重试一次。 + # 这样即便服务器上实际启动的是另一个 Chrome 可执行文件,也能自动修正 driver 版本。 + retry_major_version = _extract_browser_major_version_from_error(str(exc)) + if retry_major_version and retry_major_version != detected_major_version: + logger.warning( + f"ChromeDriver 与浏览器版本不匹配,准备按浏览器主版本 {retry_major_version} 自动重试一次" + ) + return uc.Chrome(options=options, version_main=retry_major_version) + raise + + def download_image(url, session): """使用同步的 session 下载图片,确保 Cookie 一致""" try: @@ -78,10 +162,10 @@ def fetch_and_create_pdf(url): options.add_argument('--disable-logging') options.add_argument('--disable-dev-shm-usage') - # 创建driver实例 - # 让 undetected_chromedriver 自动检测浏览器版本并下载匹配的 ChromeDriver - # 强制指定版本为144,以匹配服务器当前的 Chrome 版本 - driver = uc.Chrome(options=options, version_main=144) + # 创建 driver 实例。 + # 这里不再把版本硬编码成 144,而是优先跟随本机 Chrome 版本; + # 如果首次启动时仍然遇到版本不匹配,再从报错里解析真实版本自动重试。 + driver = _create_chrome_driver(options) logger.info(f"正在访问: {url}") driver.get(url)