Я не могу заставить Selenium Chrome работать в Docker с Python

У меня есть классическая проблема «это работает на моей машине»: веб-скребок, который я успешно запускал на своем ноутбуке, но с постоянной ошибкой всякий раз, когда я пытался запустить его в контейнере.

Мой минимальный воспроизводимый докеризованный пример состоит из следующих файлов:

требования.txt:

selenium==4.23.1  # 4.23.1
pandas==2.2.2
pandas-gbq==0.22.0
tqdm==4.66.2

Докерфайл:

FROM selenium/standalone-chrome:latest

# Set the working directory in the container
WORKDIR /usr/src/app

# Copy your application files
COPY . .

# Install Python and pip
USER root
RUN apt-get update && apt-get install -y python3 python3-pip python3-venv

# Create a virtual environment
RUN python3 -m venv /usr/src/app/venv

# Activate the virtual environment and install dependencies
RUN . /usr/src/app/venv/bin/activate && \
    pip install --no-cache-dir -r requirements.txt

# Switch back to the selenium user
USER seluser

# Set the entrypoint to activate the venv and run your script
CMD ["/bin/bash", "-c", "source /usr/src/app/venv/bin/activate && python -m scrape_ev_files"]

Scrape_ev_files.py (уменьшен до необходимого для воспроизведения ошибки):

import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service


def init_driver(local_download_path):
    os.makedirs(local_download_path, exist_ok=True)

    # Set Chrome Options    
    chrome_options = Options()
    chrome_options.add_argument("--headless")
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    chrome_options.add_argument("--remote-debugging-port=9222")

    prefs = {
        "download.default_directory": local_download_path,
        "download.prompt_for_download": False,
        "download.directory_upgrade": True,
        "safebrowsing.enabled": True
    }
    chrome_options.add_experimental_option("prefs", prefs)

    # Set up the driver
    service = Service()

    chrome_options = Options()
    driver = webdriver.Chrome(service=service, options=chrome_options)

    # Set download behavior
    driver.execute_cdp_cmd("Page.setDownloadBehavior", {
        "behavior": "allow",
        "downloadPath": local_download_path
    })

    return driver

if __name__ == "__main__":
    # PARAMS
    ELECTION = '2024 MARCH 5TH DEMOCRATIC PRIMARY'
    ORIGIN_URL = "https://earlyvoting.texas-election.com/Elections/getElectionDetails.do"
    CSV_DL_DIR = "downloaded_files"

    # initialize the driver
    driver = init_driver(local_download_path=CSV_DL_DIR)

команда оболочки, чтобы воспроизвести ошибку:

docker build -t my_scraper .  # (no error)
docker run --rm -t my_scraper # (error)

трассировка стека от ошибки приведена ниже. Любая помощь будет очень признательна! Я пробовал много итераций моих требований.txt и Dockerfile, пытаясь исправить это, но эта ошибка в этом месте была удручающе постоянной:

  File "/workspace/scrape_ev_files.py", line 110, in <module>
    driver = init_driver(local_download_path=CSV_DL_DIR)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/scrape_ev_files.py", line 47, in init_driver
    driver = webdriver.Chrome(service=service, options=chrome_options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/selenium/webdriver/chrome/webdriver.py", line 45, in __init__
    super().__init__(
  File "/workspace/.venv/lib/python3.12/site-packages/selenium/webdriver/chromium/webdriver.py", line 66, in __init__
    super().__init__(command_executor=executor, options=options)
  File "/workspace/.venv/lib/python3.12/site-packages/selenium/webdriver/remote/webdriver.py", line 212, in __init__
    self.start_session(capabilities)
  File "/workspace/.venv/lib/python3.12/site-packages/selenium/webdriver/remote/webdriver.py", line 299, in start_session
    response = self.execute(Command.NEW_SESSION, caps)["value"]
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/.venv/lib/python3.12/site-packages/selenium/webdriver/remote/webdriver.py", line 354, in execute
    self.error_handler.check_response(response)
  File "/workspace/.venv/lib/python3.12/site-packages/selenium/webdriver/remote/errorhandler.py", line 229, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.SessionNotCreatedException: Message: session not created: Chrome failed to start: exited normally.
  (session not created: DevToolsActivePort file doesn't exist)
  (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)

Трассировка стека сообщает, что не удалось создать сеанс браузера; потому что не удалось запустить Chrome. Это может произойти, если путь к исполняемому файлу не существует. Вам нужно будет настроить контейнер докера для установки Google Chrome (по любому пути, который вы выберете для установки), а затем указать этот путь в своем коде Python при инициализации веб-драйвера.

TheoNeUpKID 31.08.2024 02:23

проверьте, какая версия Chromium у вас установлена ​​и активно работает. Если это v128, то, скорее всего, у вас возникнут проблемы.

K J 01.09.2024 05:01
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
148
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не уверен, что это проблема, но проблема с вашим кодом Python определенно есть.

import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service


def init_driver(local_download_path):
    os.makedirs(local_download_path, exist_ok=True)

    # Set Chrome Options    
    chrome_options = Options()
    chrome_options.add_argument("--headless")
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    chrome_options.add_argument("--remote-debugging-port=9222")

    prefs = {
        "download.default_directory": local_download_path,
        "download.prompt_for_download": False,
        "download.directory_upgrade": True,
        "safebrowsing.enabled": True
    }
    chrome_options.add_experimental_option("prefs", prefs)

    # Set up the driver
    service = Service()

    chrome_options = Options()
    driver = webdriver.Chrome(service=service, options=chrome_options)

    # Set download behavior
    driver.execute_cdp_cmd("Page.setDownloadBehavior", {
        "behavior": "allow",
        "downloadPath": local_download_path
    })

    return driver

if __name__ == "__main__":
    # PARAMS
    ELECTION = '2024 MARCH 5TH DEMOCRATIC PRIMARY'
    ORIGIN_URL = "https://earlyvoting.texas-election.com/Elections/getElectionDetails.do"
    CSV_DL_DIR = "downloaded_files"

    # initialize the driver
    driver = init_driver(local_download_path=CSV_DL_DIR)

В этом коде вы повторили строку chrome_options:

# Set Chrome Options    
    chrome_options = Options()
    chrome_options.add_argument("--headless")
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    chrome_options.add_argument("--remote-debugging-port=9222")

    prefs = {
        "download.default_directory": local_download_path,
        "download.prompt_for_download": False,
        "download.directory_upgrade": True,
        "safebrowsing.enabled": True
    }
    chrome_options.add_experimental_option("prefs", prefs)

    # Set up the driver
    service = Service()

    chrome_options = Options() # REPEAT HERE
    driver = webdriver.Chrome(service=service, options=chrome_options)

Опять же, я не уверен, что это проблема, но ее удаление может избавить вас от проблем в будущем.

короткий ответ, какую версию Chrome вы используете?

K J 01.09.2024 04:57
Ответ принят как подходящий

Вы переопределяете переменную chrome_options непосредственно перед ее отправкой в ​​webdriver.Chrome(), поэтому не определены параметры, в частности --disable-dev-shm-usage (эта опция решает эту проблему).

Просто удалите chrome_options = Options() непосредственно перед инициализацией драйвера.

В качестве примечания рассмотрите возможность использования --headless=new вместо --headless, это дает функциональность, более близкую к обычному Chrome, а --headless будет устаревшим в будущих версиях.

Редактировать

Изображение, которое вы используете, — это отключение менеджера Selenium, поэтому вы получаете это предупреждение. Вы можете включить его снова, добавив ENV SE_OFFLINE=false в файл docker.

Инициализация драйвера иногда зависает и поднимает TimeoutException: Message: timeout: Timed out receiving message from renderer: 600.000. Вероятно, это связано со слишком большим количеством команд JS. Добавьте эти параметры

chrome_options.add_argument('--dns-prefetch-disable')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--enable-cdp-events')

headless или headless=new являются потенциальным сбоем при использовании текущего Chromiums V128 (если он не был повторно регрессирован) для некоторых задач, которые в настоящее время headless=old будут наиболее надежными! но я не проверял ни в каком случае, кроме создания PDF-файлов.

K J 01.09.2024 17:13

Привет, спасибо, Гай и Кей Джей. Попробовал два предложения Гая, очевидно, необходимо было удалить второе chrome_options. Затем я получил новую ошибку: запуск докера зависает с сообщением Exception managing chrome: Unable to discover proper chromedriver version in offline mode. Затем я в интерактивном режиме зашел в контейнер, чтобы проверить версию Chrome, и это действительно v128 (128.0.6613.113).

Max Power 01.09.2024 17:42

Только что попробовал еще раз с --headless вместо --headless=new, зависает на том же предупреждении, поэтому я думаю, что моим следующим шагом должно быть определение нового базового образа с совместимыми <128 версиями Chrome и chromedriver...

Max Power 01.09.2024 17:49

@MaxPower отредактировал мой ответ

Guy 01.09.2024 19:42

@KJ Я постоянно провожу тесты с headless=new и никогда не возникало проблем.

Guy 01.09.2024 19:43

@Guy, я не спорю, headless=new работает у меня с моим Chrome 127 и Edge до Chromium 128, в котором предположительно есть ошибка, из-за которой мне в настоящее время приходится использовать Edge headless=old, чтобы он работал без сбоев, так что это были оба последних 128 и текущий 128 неизвестно, когда = новый снова сослужит мне добрую службу

K J 01.09.2024 19:57

@MaxPower, если 128, попробуйте --headless=old !

K J 01.09.2024 19:58

Эй, еще раз спасибо! Обновления (Dockerfile ENV и новый chrome_options()) помогли мне обойти это зависающее предупреждение. Затем я столкнулся с ошибкой разрешений, когда мой код Python пытается удалить файл, но это было тривиально исправить, закомментировав USER seluser в моем файле Dockerfile, который, по-видимому, мне вообще не нужен, чтобы модуль Python запускался от имени пользователя root. . Принимаю и награждаю сейчас.

Max Power 02.09.2024 00:59

@kj, это того стоит, здесь нет проблем с --headless=new и моим v128, но спасибо за предложения.

Max Power 02.09.2024 00:59

Другие вопросы по теме