update
This commit is contained in:
@@ -35,6 +35,27 @@ from s2a_service import (
|
||||
from logger import log
|
||||
|
||||
|
||||
# ==================== 停止检查 ====================
|
||||
class ShutdownRequested(Exception):
|
||||
"""停止请求异常 - 用于中断浏览器操作"""
|
||||
pass
|
||||
|
||||
|
||||
def check_shutdown():
|
||||
"""检查是否收到停止请求,如果是则抛出异常"""
|
||||
try:
|
||||
import run
|
||||
if run._shutdown_requested:
|
||||
log.warning("检测到停止请求,中断当前操作...")
|
||||
raise ShutdownRequested("用户请求停止")
|
||||
except ImportError:
|
||||
pass
|
||||
except ShutdownRequested:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# ==================== 浏览器配置常量 ====================
|
||||
BROWSER_MAX_RETRIES = 3 # 浏览器启动最大重试次数
|
||||
BROWSER_RETRY_DELAY = 2 # 重试间隔 (秒)
|
||||
@@ -198,24 +219,49 @@ def cleanup_chrome_processes():
|
||||
"""清理残留的 Chrome 进程 (跨平台支持)"""
|
||||
try:
|
||||
if platform.system() == "Windows":
|
||||
# Windows: 使用 tasklist 和 taskkill
|
||||
result = subprocess.run(
|
||||
['tasklist', '/FI', 'IMAGENAME eq chrome.exe', '/FO', 'CSV'],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
|
||||
if 'chrome.exe' in result.stdout:
|
||||
# Windows: 使用 taskkill 清理 chromedriver 和 chrome
|
||||
try:
|
||||
subprocess.run(
|
||||
['taskkill', '/F', '/IM', 'chromedriver.exe'],
|
||||
capture_output=True, timeout=5
|
||||
)
|
||||
log.step("已清理 chromedriver 残留进程")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 清理无头模式的 chrome 进程 (带 --headless 参数的)
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['wmic', 'process', 'where', "name='chrome.exe' and commandline like '%--headless%'", 'get', 'processid'],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
for line in result.stdout.strip().split('\n'):
|
||||
pid = line.strip()
|
||||
if pid.isdigit():
|
||||
subprocess.run(['taskkill', '/F', '/PID', pid], capture_output=True, timeout=5)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
log.step("已清理 Chrome 残留进程")
|
||||
else:
|
||||
# Linux/Mac: 使用 pkill
|
||||
subprocess.run(
|
||||
['pkill', '-f', 'chromedriver'],
|
||||
capture_output=True, timeout=5
|
||||
)
|
||||
try:
|
||||
subprocess.run(
|
||||
['pkill', '-f', 'chromedriver'],
|
||||
capture_output=True, timeout=5
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 清理无头模式的 chrome 进程
|
||||
try:
|
||||
subprocess.run(
|
||||
['pkill', '-f', 'chrome.*--headless'],
|
||||
capture_output=True, timeout=5
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
log.step("已清理 Chrome 残留进程")
|
||||
except Exception:
|
||||
pass # 静默处理,不影响主流程
|
||||
|
||||
@@ -484,6 +530,9 @@ class BrowserRetryContext:
|
||||
if not self._should_continue:
|
||||
break
|
||||
|
||||
# 检查停止请求
|
||||
check_shutdown()
|
||||
|
||||
self.current_attempt = attempt
|
||||
|
||||
# 非首次尝试时的清理和等待
|
||||
@@ -495,8 +544,12 @@ class BrowserRetryContext:
|
||||
|
||||
# 初始化浏览器
|
||||
try:
|
||||
check_shutdown() # 再次检查
|
||||
self.page = init_browser()
|
||||
yield attempt
|
||||
except ShutdownRequested:
|
||||
self._should_continue = False
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"浏览器初始化失败: {e}")
|
||||
if attempt >= self.max_retries - 1:
|
||||
@@ -504,6 +557,11 @@ class BrowserRetryContext:
|
||||
|
||||
def handle_error(self, error: Exception):
|
||||
"""处理错误,决定是否继续重试"""
|
||||
# 如果是停止请求,直接停止
|
||||
if isinstance(error, ShutdownRequested):
|
||||
self._should_continue = False
|
||||
return
|
||||
|
||||
log.error(f"流程异常: {error}")
|
||||
if self.current_attempt >= self.max_retries - 1:
|
||||
self._should_continue = False
|
||||
@@ -550,6 +608,9 @@ def wait_for_page_stable(page, timeout: int = 10, check_interval: float = 0.5) -
|
||||
stable_count = 0
|
||||
|
||||
while time.time() - start_time < timeout:
|
||||
# 检查停止请求
|
||||
check_shutdown()
|
||||
|
||||
try:
|
||||
# 检查浏览器标签页是否还在加载(favicon 旋转动画)
|
||||
ready_state = page.run_js('return document.readyState', timeout=2)
|
||||
@@ -567,6 +628,8 @@ def wait_for_page_stable(page, timeout: int = 10, check_interval: float = 0.5) -
|
||||
stable_count = 0
|
||||
last_html_len = current_len
|
||||
time.sleep(check_interval)
|
||||
except ShutdownRequested:
|
||||
raise
|
||||
except Exception:
|
||||
time.sleep(check_interval)
|
||||
|
||||
@@ -626,11 +689,16 @@ def wait_for_element(page, selector: str, timeout: int = 10, visible: bool = Tru
|
||||
start_time = time.time()
|
||||
|
||||
while time.time() - start_time < timeout:
|
||||
# 检查停止请求
|
||||
check_shutdown()
|
||||
|
||||
try:
|
||||
element = page.ele(selector, timeout=1)
|
||||
if element:
|
||||
if not visible or (element.states.is_displayed if hasattr(element, 'states') else True):
|
||||
return element
|
||||
except ShutdownRequested:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(0.3)
|
||||
@@ -653,11 +721,16 @@ def wait_for_url_change(page, old_url: str, timeout: int = 15, contains: str = N
|
||||
start_time = time.time()
|
||||
|
||||
while time.time() - start_time < timeout:
|
||||
# 检查停止请求
|
||||
check_shutdown()
|
||||
|
||||
try:
|
||||
current_url = page.url
|
||||
if current_url != old_url:
|
||||
if contains is None or contains in current_url:
|
||||
return True
|
||||
except ShutdownRequested:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(0.5)
|
||||
|
||||
Reference in New Issue
Block a user