AWS LambdaでSeleniumを使ったスクレイピング(python3.13)

# 背景

スクレイピングを定期的に実行する場合、Lambdaなどのクラウド上で実行したいと考えています。今回、lambda環境上への環境構築に大変苦労したので、最終的にうまく言った方法をまとめたいと思います。

# 目的
Lambda上でSeleniumによるスクレイピングをするための方法をまとめる。

# 結論

ChromeとChromeWebDriverを含めたDocker Imageを作成し、デプロイすることでスクレイピングできる。

# 前提条件

Python 3.13.0
Chrome 131.0.6778.204
Chrome Driver 131.0.6778.204
Selenium 4.27.1

# 方針

Lambda上にChrome, Chrome Driverを使用した環境をデプロイするためには、大きく分けて二つの方法があります。

1. Lambda Layerとして定義する。
1. Dockerでイメージ化する。

調べると2020年前後の1.の方法が結構出てきますが、自分の環境ではエラーが出てなかなかうまくいきませんでした。そのため、2．の方法をとることにしました。

# やり方

### 基本のDockerFile

[こちら](https://github.com/umihico/docker-selenium-lambda/tree/main)のリポジトリがdockerでseleniumを利用するための環境構築コードが公開されており非常に使いやすいです。また、自動で最新版のChromeにアップデートされているようです。

``` DockerFile:DockerFile
FROM public.ecr.aws/lambda/python@sha256:f5b51b377b80bd303fe8055084e2763336ea8920d12955b23ef8cb99dda56112 as build
RUN dnf install -y unzip && \
    curl -Lo "/tmp/chromedriver-linux64.zip" "https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.204/linux64/chromedriver-linux64.zip" && \
    curl -Lo "/tmp/chrome-linux64.zip" "https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.204/linux64/chrome-linux64.zip" && \
    unzip /tmp/chromedriver-linux64.zip -d /opt/ && \
    unzip /tmp/chrome-linux64.zip -d /opt/

FROM public.ecr.aws/lambda/python@sha256:f5b51b377b80bd303fe8055084e2763336ea8920d12955b23ef8cb99dda56112
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
RUN pip install selenium==4.27.1
COPY --from=build /opt/chrome-linux64 /opt/chrome
COPY --from=build /opt/chromedriver-linux64 /opt/
COPY main.py ./
CMD [ "main.handler" ]

```

DockerFileは、Chromeのバージョンによりcurlコマンドのコマンドが変わります。最新版は下のリンクが参考になります。
[https://github.com/umihico/docker-selenium-lambda/blob/main/Dockerfile](https://github.com/umihico/docker-selenium-lambda/blob/main/Dockerfile)


### 独自パッケージなどの追加
独自のパッケージを追加する場合には、DockerFileに追記することでDockerイメージに組み込むことができます。
``` DockerFile:DockerFile追記

# ライブラリのインストール(例)
COPY requirements.txt ./
RUN pip install -r requirements.txt -t .

# ファイルのコピー(例)
COPY _libs/ ./_libs/

```


### 呼び出し部分
DockerFilleでlambda関数の入りを`main.handler`と指定しているため、main.py>def handler関数を実装します。

``` python:main.py
from selenium import webdriver
from tempfile import mkdtemp
from selenium.webdriver.common.by import By

def handler(event=None, context=None):
    options = webdriver.ChromeOptions()
    options.binary_location = '/opt/chrome/chrome' # ダウンロードしたChromeのファイル指定
    service = webdriver.ChromeService("/opt/chromedriver") # ダウンロードしたChromeDriverのファイル指定

    # Chromeの起動オプション
    # 一覧ページ→https://peter.sh/experiments/chromium-command-line-switches/
    options.add_argument("--headless=new") # ヘッドレスモードで起動する。
    options.add_argument('--no-sandbox')
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1280x1696")
    options.add_argument("--single-process")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--disable-dev-tools")
    options.add_argument("--no-zygote")
    options.add_argument(f"--user-data-dir={mkdtemp()}")
    options.add_argument(f"--data-path={mkdtemp()}")
    options.add_argument(f"--disk-cache-dir={mkdtemp()}")
    options.add_argument("--remote-debugging-port=9222")

    chrome = webdriver.Chrome(options=options, service=service)
    chrome.get("https://example.com/")

    return chrome.find_element(by=By.XPATH, value="//html").text

```