У меня есть классическая проблема «это работает на моей машине»: веб-скребок, который я успешно запускал на своем ноутбуке, но с постоянной ошибкой всякий раз, когда я пытался запустить его в контейнере.
Мой минимальный воспроизводимый докеризованный пример состоит из следующих файлов:
требования.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.)
🤔 А знаете ли вы, что...
Синтаксис Python известен своей простотой и читаемостью.
Я не уверен, что это проблема, но проблема с вашим кодом 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_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')