AWS Lambda（python）で Selenium を動かしてハマった点まとめ

# はじめに

Python を用いてスクレイピングをするにあたり、認証周りで躓き手っ取り早く解決するために Selenium を用いて実装したものの、Lambda で実行しようとしたときに躓いたので、メモとして残します（書きっぷりが雑になっているかもしれません…）
※ この記事は SAM を使わずに実装してます:bow:

# 作業環境

* Windows 10
* python 3.7 が望ましいけど、3.8 でも大抵はどうにでもなった
    * （使い方にもよるので、バージョン管理で切り替えられるようになっているとよい）


# ハマりポイントその１

Q. Selenium を使うにあたって、headless-chromium と chromedriver を使えばいいの？
A. この2つを圧縮して、レイヤーに登録する

## 詳細

AWS Lambda で使用できるヘッドレスブラウザとして以下が提供されています
この、chrome と chromedriver のバージョンを合わせて1つのファイルとして圧縮します
https://github.com/adieuadieu/serverless-chrome

参考記事
https://qiita.com/mishimay/items/afd7f247f101fbe25f30

## レイヤーの登録方法

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532289/07c0cc8e-b84a-8a5c-87cd-03266da1c36c.png)
1. 「レイヤーの作成」を押下する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532289/a23ed446-e0cc-c75d-6a19-06fede1b665e.png)
2. 自分がわかる名前や説明をつけて、zip ファイルをアップロードする（chrome と chromedriver をまとめて圧縮すると容易に 10MB は超えるので、S3 にアップした後登録する必要が生じる）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532289/0950257a-8b8a-9513-43df-a65d1ed0b14b.png)
3. Lambda の「レイヤーの追加」を押下して、追加したレイヤーを選択する

## 補足説明

レイヤーに登録すると「/opt/xxxx」配下にファイルが配置されることになる
例えば、chrome ディレクトリを作り、その配下に「serverless-chrome、chromedriver」のファイルを配置して圧縮したものをレイヤーに登録した場合は以下のような定義になる

### 例

```python
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options

    options = Options()
    options.binary_location = '/opt/chrome/headless-chromium'
    options.add_argument("--headless")
    options.add_argument("--no-sandbox")
    options.add_argument("--single-process")
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1280x1024")
    options.add_argument("--disable-application-cache")
    options.add_argument("--disable-infobars")
    options.add_argument("--hide-scrollbars")
    options.add_argument("--enable-logging")
    options.add_argument("--log-level=0")
    options.add_argument("--ignore-certificate-errors")

    driver = webdriver.Chrome(
        options=options, executable_path='/opt/chrome/chromedriver')
```

# ハマりポイントその２

Q. Lambda 上で Selenium が動かない
A. python 3.8 だと Amazon Linux2 になっているのか一部ライブラリがないため動かない

実行時にステータスコード 127 エラーが発生する（ザっと調べているとライブラリが不足しているかららしい）
何かしら対応策はあるかもしれないが、手っ取りばやく対応するには python3.7 の環境で実行するようにすればよい

# ハマりポイントその３

Selenium で全部実装するとクッソ遅い

## 現象

メモリ 512MB で70ページ、約2100件(1ページ辺り30件)のテーブルをパースして dynamoDB に登録する処理を実施するのに12分掛かった

## 原因

Selenium で element をガリごり操作するのはとてもコストが掛かった

## 対策

例えばページを読み込んだ後、パース以降の処理を BeautifulSoup に任せる
※ lxml は xPath が使えて便利な反面 C言語を用いているため環境依存する。そのため、Amazon Linux2 環境でデプロイするなど、手間が増えてしまうことが実装中にわかったため BeautifulSoup を用いた

例）

```python
driver.get('https://example.com')
html = BeautifulSoup(driver.page_source, 'html.parser')
table = html.select_one('table')
rows = table .findAll('tr')
for row in rows:
    cells = row.findAll('td')
# todo 
driver.quit()
```

結果として、同条件下で 12分 => 2分と処理速度の改善が見られた

# 実装中に理解した小ネタ

lambda の python を実行する際、「/opt/python/」配下にあるライブラリを読み込むようになっているらしい
そのため、「pip install -r ./requirements.txt -t .」等でlibを吐き出して zip 圧縮したものをレイヤーに登録しておくことで、実行するソースファイルのみをアップロード出来るようになり Lambda の WEB の画面からソースの編集が出来るようになった

# さいごに

Lambda で Selenium って動かせると思っていなくて、ローカル PC の WSL に cron 仕込もうかって思っていたけれど、そんなことをしなくても良いことを知った

レイヤーすごい:laughing:
次はこれらを template.yml やレイヤーの yml ファイルを用意して、sam を用いてデプロイ出来るようになりたいなと思いました:sweat: