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

Celery任务中运行无头Firefox时WebDriver意外关闭报错

Flask+Celery无头Firefox运行Selenium报错:Process unexpectedly closed with status 127

问题详情

在Flask应用中通过Celery任务以无头模式运行Selenium Firefox时,触发错误:

selenium.common.exceptions.WebDriverException: Message: Process unexpectedly closed with status 127

相关代码

from selenium.webdriver.firefox.service import Service
import sys, os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep
from dotenv import load_dotenv
from check_email import EmailedReferrals, Referrals
from sqlalchemy import create_engine, Column, Integer, String, Date, Text
from sqlalchemy.orm import declarative_base 
from sqlalchemy.orm import sessionmaker
from datetime import datetime
import logging

logger = logging.getLogger('celery')
logger.setLevel(logging.DEBUG)

load_dotenv()

env_conf = os.getenv("CONFIG")
Base = declarative_base()

if env_conf == "dev":    
    driver = webdriver.Firefox()
    DATABASE_URL = os.getenv('DEV_DATABASE_URL')    
else: 
    # Set up Firefox options
    options = Options()
    # Set window size
    options.add_argument("--window-size=1920,1080")
    options.add_argument("-headless")
    # Include the path to the Firefox binary
    firefox_binary_path = '/usr/bin/firefox'
    geckodriver_path = '/home/fusion/bin/geckodriver'
    options.binary_location = firefox_binary_path
    service = Service(executable_path=geckodriver_path)
    driver = webdriver.Firefox(service=service, options=options)
    DATABASE_URL = os.getenv('PROD_DATABASE_URL')
engine = create_engine(DATABASE_URL)

Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

def get_row_xpath(row, cell): #pulls xpath of rows and cells from Incoming Referrals View
    ctl = str(row).zfill(2)
    action = row - 3
    id_cell = f"""/html/body/form/table/tbody/tr[4]/td[2]/table/tbody/tr/td/table/tbody/tr[2]/td/div/table/tbody\
        /tr/td[3]/div/table/tbody/tr[2]/td/div/table/tbody/tr[{row}]/td[{cell}]"""
    select_element = f"""//*[@id="ucViewGrid_dgView_ctl{ctl}_Actions_{action}_0_0_ActionItems"]"""
    go_button = f"""#ucViewGrid_dgView_ctl{ctl}_Actions_{action}_0_0_ActionButton > input:nth-child(1)"""
    return id_cell, select_element, go_button

def main(referral_id_from_flask, comment):
    print('hello from main')
    logger.info("hello from main")
    driver.get("https://www.example.com")
    #Click Account and sign in
    driver.find_element(By.XPATH, '//*[@id="UserNameTextBox"]').send_keys(os.getenv('USERNAME'))
    driver.find_element(By.XPATH, '//*[@id="PasswordTextBox"]').send_keys(os.getenv('PASSWORD'))
    driver.find_element(By.XPATH, '//*[@id="btnLoginHack"]').click()
    sleep(2)
    driver.find_element(By.XPATH, '//*[@id="ctl13"]').click()
    #loop through table
    cell = 3
    for row in range(3, 52):
        logger.info(f"Looping: {row}")
        print(f"Loop #: {row}")
        id_cell = driver.find_element(By.XPATH, get_row_xpath(row, cell)[0]).text
        referral_id = id_cell.split('\n')[0].strip()
        #find row with the correct
        if referral_id == referral_id_from_flask:
            #Select View Online Referral
            select_element = driver.find_element(By.XPATH, get_row_xpath(row, cell)[1])
            select = Select(select_element)
            select.select_by_visible_text("View")
            #click go
            driver.find_element(By.CSS_SELECTOR, get_row_xpath(row, cell)[2]).click()
            #we are in View Online Referral of the patient
            #Find select box
            #select value 0  which is accept patient
            select_element = driver.find_element(By.XPATH, """//*[@id="dgProviders_ctl02_ddResponse"]""")
            select = Select(select_element)
            select.select_by_value("1")
            sleep(0.5)
            #comment box
            # wait up to 10 seconds for the element to be present and clickable
            comment_box = WebDriverWait(driver, 10).until(
                EC.element_to_be_clickable((By.XPATH, '//*[@id="dgProviders_ctl02_txtProviderComments"]'))
            )
            comment_box.clear()
            sleep(0.5) # Add a small delay here
            comment_box.send_keys(comment)
            sleep(0.5) # Add a small delay here
            #click send response
            #driver.find_element(By.XPATH, """//*[@id="ButtonBarSendResponse"]""").click()
            #add to query
            r = session.query(Referrals).filter(Referrals.referral_id == int(referral_id)).first()
            r.referral_acceptence_status = "accepted"
            session.commit()
            session.close()
            print('done')
            break

if __name__ == "__main__":
    
    referral_id_from_flask = sys.argv[1]
    comment = sys.argv[2]
    main(referral_id_from_flask, comment)

完整堆栈跟踪

[2023-04-17 09:35:13,375: ERROR/ForkPoolWorker-2] Traceback (most recent call last):
  File "/home/fusion/scrapers/care_portal/accept.py", line 43, in <module>
    driver = webdriver.Firefox(service=service, options=options)
  File "/home/fusion/flaskapp/venv/lib/python3.10/site-packages/selenium/webdriver/firefox/webdriver.py", line 199, in __init__
    super().__init__(command_executor=executor, options=options, keep_alive=True)
  File "/home/fusion/flaskapp/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 286, in __init__
    self.start_session(capabilities, browser_profile)
  File "/home/fusion/flaskapp/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 378, in start_session
    response = self.execute(Command.NEW_SESSION, parameters)
  File "/home/fusion/flaskapp/venv/lib/python3.10/site-packages/selenium/webdriver/remote/webdriver.py", line 440, in execute
    self.error_handler.check_response(response)
  File "/home/fusion/flaskapp/venv/lib/python3.10/site-packages/selenium/webdriver/remote/errorhandler.py", line 245, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: Process unexpectedly closed with status 127

原因分析与解决方法

错误码127表示找不到执行依赖文件或权限不足,针对Celery+无头Firefox场景,按以下步骤排查修复:

1. 验证路径与执行权限

  • 检查Firefox二进制文件是否存在且可执行:
    ls -l /usr/bin/firefox
    /usr/bin/firefox --version
    
  • 确保Geckodriver有执行权限:
    chmod +x /home/fusion/bin/geckodriver
    /home/fusion/bin/geckodriver --version
    
  • 确认Celery运行用户(如www-data、部署用户)拥有这两个文件的执行权限。

2. 安装无头Firefox依赖库

无头模式下Firefox需要额外系统依赖,Debian/Ubuntu系统执行:

apt-get install -y libgtk-3-0 libdbus-glib-1-2 libxt6 libx11-xcb1 libxcomposite1 libxdamage1 libxfixes3 libxi6 libxrandr2 libasound2 libpangocairo-1.0-0 libatk1.0-0 libcairo2 libcups2 libdrm2 libgbm1 libnss3 libxss1

3. 优化Firefox无头配置

替换旧版无头参数,添加适配服务器环境的配置:

options = Options()
options.add_argument("--headless=new")  # Firefox 102+推荐的新版无头模式
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")  # 规避沙箱权限限制
options.add_argument("--disable-dev-shm-usage")  # 解决临时空间不足问题
options.binary_location = '/usr/bin/firefox'

4. 调整WebDriver初始化位置

当前代码在模块全局初始化driver,Celery多进程环境会导致资源冲突,应将driver初始化移到main函数内部,并确保任务结束后关闭:

def main(referral_id_from_flask, comment):
    # 初始化driver
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--window-size=1920,1080")
    options.add_argument("--disable-gpu")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    options.binary_location = '/usr/bin/firefox'
    service = Service(executable_path='/home/fusion/bin/geckodriver')
    driver = webdriver.Firefox(service=service, options=options)
    
    try:
        # 原有业务逻辑
        driver.get("https://www.example.com")
        # ...后续操作
    finally:
        driver.quit()  # 确保任务结束后关闭浏览器

5. 确认Celery环境变量配置

确保Celery进程能正确读取.env文件,或在Celery启动时显式传递环境变量,避免路径、账号等配置读取失败。


内容的提问来源于stack exchange,提问作者Mike C.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 00:33:11