EC2＋Python＋Selenium+Cronで仮想サーバ上でスクレイピングを定期実行する

# はじめに
AWS EC２を使用してSeleniumを活用したスクリプトをCronを使って定期実行する手順を紹介します。
当初は、AWS Lambda上で定期実行することを考えていましたが、Python3.7でseleniumを起動している記事が多く、うまくスクレイピングが出来ず悩んでいました。
> 23年10月ごろにPython3.7のサポートが終了しているため、うまくいかないと考えられます。

EC2で仮想サーバーを立て、そのサーバーにChromedriver等をインストールして、Cronで定期実行する方法だとうまく実行できたため、今回記事にしました。


# 簡単な流れ

1. **EC2インスタンスのセットアップ** - AWSで無料枠のLinux EC2インスタンスを立ち上げます
2. **必要なツールのインストール** - Python、Selenium、およびWebDriverをEC2インスタンスにインストールします
3. **Pythonスクリプトの作成** - 定期実行したいPythonスクリプトを作成します
4. **Cronの設定** - スクリプトを定期的に実行するためにCronを設定します

## 現在の環境

- Windows 11
- Python 3.11.5
- Anaconda 3
- VSCode


## ステップ1: EC2インスタンスのセットアップ

EC2からEC2インスタンスを作成・起動し、WindowsのコマンドプロンプトからSSHで接続します。
EC2インスタンスの作成からPython3のインストールまでは、以下の記事を参考にさせていただきました。
  
 https://note.com/yuu________/n/nbe90710b842b  
  
> SSHでのログインはSSHのキーペアを保存しているディレクトリに移動してから、実行します。  
 例えばキーペアのPATHが　`C:\Users\Username\Documents\key.pem`　だと、以下のようにするとログインでき ます。  
>```Bash:Prompt
> # cd C:\Users\Username\Documents
> # ssh -i key.pem ec2-user@[パブリックIPv4アドレス]


## ステップ2: 必要なツールのインストール

Python3のインストール後、実行したいスクリプトに必要なツール等をインストールしていきます。
Chromedriverのインストールからselenium等のツールのインストールまでは、先ほどと同様以下の記事を参考にさせていただきました。

https://note.com/yuu________/n/n75d185d30e26

> ここでは、ChromeとChromedriverのバージョンを一致、もしくはChromedriverのバージョンがChromeよりも小さい数値になるようにしてください。私は、それぞれのバージョンを　`Chrome:121.0.6261.57`　に揃えました。


## ステップ3: Pythonスクリプトの作成
定期時刻させたいPythonスクリプト `test.py` を作成し、vimでスクリプトを貼り付けます。
```Bash:ec2-user
# vi test.py   
```

これでスクレイピングができるか確認してみます。
```Bash:ec2-user
# python test.py
```
ここで、Chromedriverの設定不良でスクレイピングがうまくいかない場合、スクリプト内のChromedriverのPATHの指定が出来ていない可能性があります。その場合、一度ChromedriverのPATHを調べて、そのPATHをスクリプトに貼り付けて指定してください。

```Bash:ec2-user
# find -name "Chromedriver"

./.pyenv/versions/3.11.5/lib/python3.11/site-packages/chromedriver_binary/chromedriver
```
今回、ChromedriverのPATHが`./.pyenv/versions/3.11.5/lib/python3.11/site-packages/chromedriver_binary/chromedriver`と分かったので、test.pyのchromedriverのPATH指定をこれに変更していきます。
以下はサンプルコードです。

```Bash:Vim（test.py)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By

options = webdriver.ChromeOptions()
options.add_argument("--headless")

service = webdriver.chrome.service.Service(executable_path='./.pyenv/versions/3.11.5/lib/python3.11/site-packages/chromedriver_binary/chromedriver')
driver = webdriver.Chrome(service=service, options=options)

driver.get('https://www.google.co.jp')
element_text=driver.find_element(By.CLASS_NAME,"MV3Tnb").text

print(element_text)

driver.quit()

```
`Googleについて`と出力されれば正常に動いています。

# ステップ4: Cronの設定
次は、このスクリプトを定期実行するための設定を行います。定期実行には`Cron`を用います。指定した時間に、指定したプログラムを動かしてくれる仕組みです。

https://konsuke-blog.com/?p=417

こちらの記事を参考にしました。

## 1. Cronのインストール
Cronをインストールしていきます。
```Bash:ec2-user
# sudo yum install cronie -y
Complete!

# sudo systemctl enable crond.service
# sudo systemctl start crond.service
# sudo systemctl status crond | grep Active
     Active: active (running) since ~
```

https://www.next.inc/articles/2023/aws-ec2amazon-linux-2023%E3%81%ABcrontab%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B.html

## 2. 定期実行の設定

インストールが終了したら、次は定期実行の設定をしていきます。

```Bash:ec2-user
$ Crontab -e
```
これを実行すると、vimエディタが開きます。空白のエディタが開くと思いますが、そこに以下の文章を作成していきます。とりあえず、以下をコピペしてください。

```Bash:Vim（Crontab -e)
SHELL=
PATH=
HOME=

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed

# run-parts
#01 * * * * root run-parts /etc/cron.hourly
#05 2 * * * root run-parts /etc/cron.daily
#20 2 * * 0 root run-parts /etc/cron.weekly
#40 2 1 * * root run-parts /etc/cron.mointhly


```
コピペ後、各項目を入力していきます。
`SHELL` `PATH` `HOME`には、自身の環境に応じて入力してください。
項目は`$ printenv`でPATH等を取得します。
```Bash:ec2-user
# printenv

SHELL=/bin/bash
PYENV_SHELL=...
HISTCONTROL=...
SYSTEMD_COLORS=...
HISTSIZE=...
HOSTNAME=...
PWD=...
LOGNAME=...
XDG_SESSION_TYPE=...
MOTD_SHOWN=...
HOME=/home/ec2-user
...
...
...
```

## 3. PATHの確認
実行したいスクリプトのPATHと、Python3の実行環境を確認します。それぞれのPATHをメモしてください。
```Bash:ec2-user
# find `pwd` -name "test.py"
    /home/ec2-user/test.py
```

```Bash:ec2-user
# which python3
    ~/.pyenv/shims/python3
```

## 4. エラーログファイルの作成
エラーログを確認するためのファイルを作成します。
```Bash:ec2-user
$ vi cron-error.log
```
空白のまま保存して終了します。このファイルのPATHもメモしてください。

## 5. 定期実行の時間の設定
```Bash:ec2-user
$ Crontab -e
``` 
でもう一度Vimエディタを開きます。

```Bash:Vim（Crontab -e)
SHELL=/bin/bash
PATH=/home/ec2-user/.pyenv/shims:/home/ec2-user/.pyenv/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
HOME=/home/ec2-user

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed

# run-parts
#01 * * * * root run-parts /etc/cron.hourly
#05 2 * * * root run-parts /etc/cron.daily
#20 2 * * 0 root run-parts /etc/cron.weekly
#40 2 1 * * root run-parts /etc/cron.mointhly

37 14 * * * ~/.pyenv/shims/python3 /home/ec2-user/test.py > /home/ec2-user/cron-error.log 2>&1
```
`# run-parts`の項目の下に、実行したいスクリプトと、時間を入力していきます。
時間の項目は、
`分` `時間` `日` `月` `曜日` の順番になっています。指定しない項目には`*`を入力してください。

例えば上記のスクリプトでは、`37 14 * * *` なので、14時37分になると右の文章が定期実行されます。
１０分毎に実行したい場合は、`*/10 * * * * `のように入力します。
詳しい設定はこちらを確認してみてください。

https://www.server-memo.net/tips/crontab.html



時間の入力後は、`[Python3のPATH] [test.pyのPATH] > [cron-error.logのPATH] 2>&1`と入力します。
ここでは「Python3を使って`test.py`を実行し、その結果とエラーメッセージを`cron-error.log`に出力する」ことを行っています。

## 6. ログへの書き込み権限の設定
定期実行の結果を `cron-error.log` に書き込む操作を行っているため、このファイルに書き込み可能となるように権限を付与します。

```Bash:ec2-user
# ls -la
-r--r--r--.  1 ec2-user ec2-user     0   cron-error.log
-rw-------.  1 ec2-user ec2-user  7957   test.py
```
書き込みが必要なため、`chmod`で`644`を設定します。
```Bash:ec2-user
$ chmod 644 cron-error.log
```
また、test.pyにも実行権限が必要なため、`700`を設定します。
```Bash:ec2-user
$ chmod 700 test.py
```
>権限については以下を参照してください。
>
>https://web.tku.ac.jp/~densan/local/permission/permission.htm


## 7. ログの確認
実際に実行されるかを確認します。
```Bash:ec2-user
$ sudo tail -f /var/log/cron

Jan 01 00:00:00 ip---- CROND[10831]: (ec2-user) CMD (~/.pyenv/shims/python3 /home/ec2-user/test2.py > /home/ec2-user/cron-error.log 2>&1)
Jan 01 00:00:04 ip---- CROND[10828]: (ec2-user) CMDEND (~/.pyenv/shims/python3 /home/ec2-user/test2.py > /home/ec2-user/cron-error.log 2>&1)
```
`-f` をつけることで、リアルタイムに実行状況を確認できます。

結果やエラーログの確認は以下のコマンドを実行します。
```Bash:ec2-user
$ cat cron-error.log
```

# まとめ
EC2上でPythonとSeleniumのスクリプトをCronで定期実行する方法を紹介しました。
これにさらにAWSの `EventBridge` を用いることで、必要最小限の時間だけインスタンスを起動し、スクリプトの定期実行が行えるようになります。



# 参考記事

https://note.com/yuu________/n/nbe90710b842b

https://note.com/yuu________/n/n75d185d30e26

https://konsuke-blog.com/?p=417

https://www.server-memo.net/tips/crontab.html

https://web.tku.ac.jp/~densan/local/permission/permission.htm