えらくハマったのでメモです。以下のようにするとできました。
Dockerfile
FROM public.ecr.aws/lambda/python:3.12 # Seleniumをインストールする。 RUN python3.12 -m pip install selenium -t . # selenium-managerを使ってChromeとChromeDriverをダウンロードする。 RUN /var/task/selenium/webdriver/common/linux/selenium-manager --browser chrome --cache-path /var/task # Chromeの依存関係をインストールする。 # 参考: https://qiita.com/hideki/items/d1ff83e7e82afc0c0502 RUN dnf install -y atk cups-libs gtk3 libXcomposite alsa-lib \ libXcursor libXdamage libXext libXi libXrandr libXScrnSaver \ libXtst pango at-spi2-atk libXt xorg-x11-server-Xvfb \ xorg-x11-xauth dbus-glib dbus-glib-devel nss mesa-libgbm \ libgbm libxkbcommon libdrm # 以下、デフォルトのコード COPY app.py requirements.txt ./ RUN python3.12 -m pip install -r requirements.txt -t . # Command can be overwritten by providing a different command in the template directly. CMD ["app.lambda_handler"]
Lambda側のコードは以下のような感じです。
app.py
from tempfile import mkdtemp import glob from selenium import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By print("chrome起動中") chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--headless=new') chrome_options.add_argument('--disable-gpu') chrome_options.add_argument('--disable-dev-shm-usage') chrome_options.add_argument('--disable-dev-tools') chrome_options.add_argument('--no-zygote') chrome_options.add_argument('--window-size=1280x1696') chrome_options.add_argument(f"--user-data-dir={mkdtemp()}") chrome_options.add_argument(f"--data-path={mkdtemp()}") chrome_options.add_argument(f"--disk-cache-dir={mkdtemp()}") chrome_options.add_argument('--no-sandbox') chrome_options.add_argument('--hide-scrollbars') chrome_options.add_argument('--enable-logging') chrome_options.add_argument('--log-level=0') chrome_options.add_argument('--v=99') chrome_options.add_argument('--single-process') # chrome_options.add_argument('user-agent=' + ...) # "/var/task/chrome/linux64/121.0.6167.85/chrome" のような場所にある実行ファイルを指定する。 chrome_options.binary_location = glob.glob("/var/task/chrome/linux64/*/chrome")[0] service = ChromeService(glob.glob("/var/task/chromedriver/linux64/*/chromedriver")[0]) driver = webdriver.Chrome(service=service, options=chrome_options) print("chrome起動完了") def lambda_handler(event, context): driver.get("https://example.com/") body = WebDriverWait(driver, 10).until(lambda d: d.find_element(By.TAG_NAME, "body")) return body.text
Chromeの起動オプションは以下の記事を参考にしました。
https://qiita.com/hideki/items/d1ff83e7e82afc0c0502
上記の構成のCloudFormation(SAM)のサンプル
https://github.com/778a0a/docker-lambda-selenium-chrome-example
若干の補足
selenium-managerで良い感じのバージョンのChromeとChromeDriverをダウンロードしているところがポイントです。selenium-managerでダウンロードした実行ファイルはデフォルトだと/root/.cache/selenium/
以下に保存されるのですが、Lambda環境だとこの場所にあるファイルは実行できないようなので、--cache-path
を指定してpythonファイルと同じ/var/task/
以下に保存しています。(/opt
以下でも問題ない?)
Lambda側では、ダウンロードした実行ファイルのパスを明示的に指定して実行します。バージョン番号は不定なので、globで良い感じに解決しています。
以上です。