Lambda関数でPythonとSelenium使ってサーバレスでスクレイピングする

# はじめに
こんにちは。先日、とある企業のとあるリードテックの方とお話しする機会がありました。今までにない切り口で質問いただいたり、すごく勉強にも気づきにもなって楽しくて、あの話もしたかったなとか、今までにない感情がわきました :smile:

あぁいうのって、**話術**っていうんですかね :thinking: すごかったです :smile: 

---

さて、今回は**Lambda**を使って**Selenium**によるスクレイピングを行っていこうと思います。
私は、**GoogleAppsScript**をよく使うので、簡単はものはそれでいいのですが、ちゃんとした**テスト**をするには、**Selenium**等使うべきだとは思います :pray:

**Heroku**に**Laravel**入れて**Selenium**使おうとかも思ったのですが、せっかくなので、今まで触ったことないものに挑戦しようと、今回の運びになりました。

**Lambda**も**Python**も使ったことないので、同じく初心者の方にやさしい記事になると思います。
参考になれば幸いです :bow:

# 前提条件
1. **Windows 10 Pro**
2. **WSL2** (20.04.2 LTS (Focal Fossa))
3. **Python3** インストール済み
4. **pip**インストール済み

# 方針
1. 必要なライブラリをダウンロード
2. Lambdaレイヤー用のzipファイル作成
3. Lambdaレイヤーに2をアップロード
4. Lambda関数作成
5. Lambdaレイヤー追加
6. 関数にプログラムを書く
7. デプロイ
8. 設定変更
9. テスト

# Lambda関数でPythonとSelenium使ってサーバレスでスクレイピングする
## 1. 必要なライブラリをダウンロード

ダウンロードするのは以下3つ。
ここで、`chromium`と`chromedriver`は最新版を使用すると後続でLambdaレイヤーアップロード時にサイズがでかくてS3に置いてくれって怒られるので、あえて古めを採用:pray:

1. selenium
2. chromium
3. chromedriver

フォルダ構造はこんな感じ

```sh
$ tree -L 5
.
├── headless
│   └── python
│       └── bin
│           ├── chromedriver
│           └── headless-chromium
└── python
    └── lib
        └── python3.8
            └── site-packages
                ├── selenium
                ├── selenium-3.141.0.dist-info
                ├── urllib3
                └── urllib3-1.26.6.dist-info
```

### 1.1. selenium

```sh
$ pip install selenium -t python/lib/python3.8/site-packages
```

### 1.2. chromium
```sh
$ curl -SL https://github.com/adieuadieu/serverless-chrome/releases/download/v1.0.0-37/stable-headless-chromium-amazonlinux-2017-03.zip > headless-chromium.zip
$ unzip -o headless-chromium.zip -d .
$ rm headless-chromium.zip
```

### 1.3. chromedriver

```sh
$ curl -SL https://chromedriver.storage.googleapis.com/2.37/chromedriver_linux64.zip > chromedriver.zip
$ unzip -o chromedriver.zip -d .
$ rm chromedriver.zip
```

## 2. Lambdaレイヤー用のzipファイル作成

以下のように`headless`フォルダと`python`フォルダをzip圧縮します。

```
$ tree-L 5
.
├── headless
│   └── python
│       └── bin
│           ├── chromedriver
│           └── headless-chromium
├── headless.zip
├── python
│   └── lib
│       └── python3.8
│           └── site-packages
│               ├── selenium
│               ├── selenium-3.141.0.dist-info
│               ├── urllib3
│               └── urllib3-1.26.6.dist-info
└── python.zip
```

## 3. Lambdaレイヤーに2をアップロード

### 3.1. Selenium
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86984/d655cfcd-868c-6482-73b9-0fb1a82d5ab4.png)


### 3.2. chromium, chromedriver
先にふれたとおり、ここで50MBを超えるとS3にアップロードしないとだめなので、旧バージョンを使用して乗り切ります :pray:

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86984/0ae51193-dd8a-8b4f-c85c-667f000e1954.png)

## 4. Lambda関数作成

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86984/d54c1268-ddc2-9901-d8ac-998d861e3d69.png)

## 5. Lambdaレイヤー追加
4で作成した関数のページ下部で追加できるので、追加します:ok_hand:

## 6. 関数にプログラムを書く

`https://example.com/` のタイトルを取得しようと思います。

```py
from selenium import webdriver

def lambda_handler(event, context):
    URL = "https://example.com/"

    options = webdriver.ChromeOptions()
    options.binary_location = "/opt/headless/python/bin/headless-chromium"
    options.add_argument("--headless")
    options.add_argument("--disable-gpu")
    options.add_argument("--hide-scrollbars")
    options.add_argument("--single-process")
    options.add_argument("--ignore-certificate-errors")
    options.add_argument("--window-size=880x996")
    options.add_argument("--no-sandbox")
    options.add_argument("--homedir=/tmp")

    browser = webdriver.Chrome(
        executable_path="/opt/headless/python/bin/chromedriver",
        options=options
    )

    browser.get(URL)
    title = browser.title
    browser.close()
    return title
```

## 7. デプロイ
`Deploy` ボタンがあるのでそれを押下します。

## 8. 設定変更
メモリが不足する場合があるらしいので256まで上げます。
あと、実行に時間がかかるため、タイムアウトの時間も増やします。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86984/739f8057-6771-e5d2-f5c2-e76bf2ab2b9a.png)


## 9. テスト
まず、テストイベントの作成を行います。

`テスト`タブに移動し、新しいイベントでテンプレート: `hello-world`を選択して保存します。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86984/4c50513b-c364-a88d-0a27-b380016d630f.png)

実行結果が以下と表示されればOKです :ok_hand:
```
Response
"Example Domain"
```

# おわりに
いろいろほかの方の記事とか見ながら進めていましたが、いかんせん一筋縄ではいかなかったです:sob:
パスが通らなかったり、ファイルがでかかったり、デプロイしないとテストできなかったり、いろいろ記事読んでそちらに書いてない部分をこの記事で補えればと思います:pray:

それでは:bow:

# 参考にさせていただいた記事
以下記事を参考にさせていただきました。ありがとうございました:pray:
トラブルシューティングの際はこちらも併せてご参照くださいませ:bow:

https://qiita.com/ichihara-development/items/5e61c3424b3176bc6096

https://qiita.com/gabiQ/items/18836611b47d2a1e56c4

https://qiita.com/ichihara-development/items/69a22e023751fef691a1

https://note.com/kei198403/n/n24d8f0bfb39a