You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker化Selenium脚本定位元素时随机冻结问题求助

问题排查与解决方案

问题背景

VPS无法安装桌面环境,通过Docker部署SeleniumBase爬虫,以有头模式(headful)运行绕过Cloudflare验证码。脚本可正常启动,但随机在元素定位环节冻结,甚至Ctrl+C无法触发shutdown handler。


一、Docker环境优化

1. 修复Xvfb与Chrome兼容性问题

原Dockerfile中Xvfb启动参数不足,易引发Chrome渲染异常导致冻结。修改CMD启动命令,补充图形渲染与权限参数:

# 替换原CMD为:
CMD ["sh", "-c", "Xvfb :99 -screen 0 1920x1080x24 -ac +extension GLX +render -noreset & python private/main.py"]
  • -ac:关闭访问控制,允许任意用户连接X服务器
  • +extension GLX +render:启用图形渲染扩展,避免Chrome渲染崩溃
  • -noreset:保持X服务器会话,减少资源泄漏

2. 优化Chrome启动参数

在Driver初始化时添加Docker环境专属参数,解决资源限制与稳定性问题:

driver = Driver(
    binary_location="/usr/bin/google-chrome",
    undetected=True, browser='chrome', no_sandbox=True,
    agent=random.choice(user_agents),
    do_not_track=True, headless=False,
    extra_args=[
        "--disable-dev-shm-usage",  # 禁用/dev/shm,规避Docker内存限制
        "--disable-gpu",            # 禁用GPU渲染,有头模式下无需GPU加速
        "--start-maximized",        # 最大化窗口,避免元素因视口问题无法定位
        "--disable-features=VizDisplayCompositor"  # 禁用合成器,减少渲染冲突
    ]
)

3. 清理冗余依赖

原Dockerfile重复执行apt-get update且安装了不必要依赖(如python3-tk),修改后减少镜像体积与冲突概率:

# 修改后的系统依赖安装步骤
RUN apt-get update && apt-get install -y \
    wget \
    gnupg2 \
    curl \
    unzip \
    libnss3 \
    libxss1 \
    fonts-liberation \
    xvfb \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

二、代码层面修复

1. 统一使用显式等待,避免无限阻塞

原代码中driver.find_element无超时设置,元素未加载时会无限等待导致冻结。替换为WebDriverWait统一处理:

# 替换原for循环中的token_link获取逻辑
for i in range(1, 4):
    try:
        token_link = WebDriverWait(driver, 3).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, f'a.custom-p8lifi:nth-child({i})'))
        )
        # 后续解析逻辑不变
    except TimeoutException:
        logging.warning("Token %s 定位超时", i)
        continue

2. 异步代码中隔离同步阻塞操作

Selenium API为同步实现,在async函数中直接调用会阻塞事件循环,导致无法响应Ctrl+C等信号。使用asyncio.to_thread将同步操作放入线程池:

async def fetch_token_hrefs():
    driver = None
    last_restart_time = time.time()
    # 注册信号处理
    _register_shutdown_handler(driver)
    
    try:
        while True:
            # ... driver重启逻辑 ...
            try:
                # 将driver同步操作放入线程池
                tokens = await asyncio.to_thread(_fetch_token_batch, driver)
                # ... 日志、保存、yield逻辑 ...
            except Exception as e:
                logging.error("Error fetching tokens: %s", e)
                await asyncio.to_thread(driver.refresh)
                await asyncio.sleep(random.randint(1, 2))
    except Exception as e:
        logging.error("Failed to initialize driver: %s", str(e))
        await bot.send_message(CHANNEL_ID, f"🚨 Driver Initialization Error: {str(e)}")
        if driver:
            driver.quit()

# 提取同步操作到单独函数
def _fetch_token_batch(driver):
    driver.uc_open_with_reconnect()
    if "Cloudflare" in driver.page_source:
        logging.info("Cloudflare captcha detected. Attempting to click the captcha.")
        driver.uc_gui_click_captcha()
    logging.info("Passed the cloudflare captcha")
    tokens = []
    WebDriverWait(driver, 5).until(
        EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'a.custom-p8lifi'))
    )
    # ... 元素解析逻辑 ...
    return tokens

3. 完善信号处理与资源清理

添加信号捕获函数,确保Ctrl+C能正常终止driver进程:

import signal

def _register_shutdown_handler(driver_ref):
    def handle_shutdown(signum, frame):
        logging.info("Received shutdown signal, exiting...")
        if driver_ref:
            try:
                driver_ref.quit()
            except:
                pass
        exit(0)
    signal.signal(signal.SIGINT, handle_shutdown)
    signal.signal(signal.SIGTERM, handle_shutdown)

4. 增加driver健康检查

在每次循环前检查driver是否存活,避免使用已崩溃的实例:

current_time = time.time()
if driver is None or (current_time - last_restart_time) > 300 or not driver.is_alive():
    if driver:
        try:
            driver.quit()
        except:
            pass
    # 重新初始化driver
    driver = Driver(...)

三、调试与排查手段

  1. 开启Chrome详细日志:在Driver初始化时添加日志参数,定位冻结根源:
extra_args=[
    "--log-level=verbose",
    "--enable-logging",
    "--v=1",
    "--log-path=/app/chrome.log"
]
  1. 增加步骤日志:在每个元素定位前后添加时间戳日志,追踪冻结节点:
logging.info("Starting to locate token %s at %s", i, datetime.now())
token_link = WebDriverWait(driver, 3).until(...)
logging.info("Located token %s at %s", i, datetime.now())
  1. 进程系统调用追踪:在Docker容器中使用strace追踪进程行为,定位冻结点:
strace -p <python进程PID> -o strace.log

内容的提问来源于stack exchange,提问作者Hope

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 11:22:04