この記事では、Pythonで作成したスクレイピング(Selenium
, Beautiful Soup
)のプログラムを、サーバー上(Heroku)に設置し、決まった時間に定期実行させる方法を紹介しています。
今回、AWSやGCPと言った数あるIaaSやPaaSの中で、HerokuというPaaSを選んだ理由は4つあります。
- インフラ面をそこまで意識せずに使える
- 定期実行させる簡単なスクレイピングであれば、ほぼ無料で使えること
- 利用者が多く、ドキュメントが充実してる(大事)
- PaaSとしての歴史が長い
Herokuについてもっと詳しく知りたい方は、こちらの記事に良くまとめられていました。
ローカルで動作確認する
ローカルで動作しないプログラムは、Heroku上で絶対に動作しません。必ずローカルでもちゃんと動くか確認しましょう。
Yahooの天気サイトをSelenium
でクローリングして、『今日の日本の天気予報の要約』部分をBeautiful Soup
でスクレイピングして、Web APIのLINE Notify
をrequests
で叩いて、LINEに通知させます。
$ mkdir {{フォルダ名}}
$ python3 -m venv {{フォルダ名}}; cd {{フォルダ名}}
┣ bin/
┣ include/
┣ lib/
┗ pyvenv.cfg
$ source bin/activate
Selenium
とBeautiful Soup
、requests
を仮想環境にインストールする(開発フォルダ名)$ pip install selenium
(開発フォルダ名)$ pip install beautifulsoup4
(開発フォルダ名)$ pip install requests
以降、ターミナルにおいて(開発フォルダ名)$ UNIXコマンド
の(開発フォルダ名)
部分は省略しますが、開発環境は仮想環境のままで、読み進めて下さい。
main.py
の作成$ touch main.py
┣ bin/
┣ include/
┣ lib/
┣ main.py NEW
┗ pyvenv.cfg
main.py
をSelenium
でヘッドレスモードでクローリングし、Beautiful Soup
でスクレイピングしてLINEに通知させるプログラムに書き換えます。
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# ローカルに保存しているChrome Driverを指定(※デプロイするときはコメントアウトする)
driver_path = '{{Chrome DriverのPATH}}'
# Heroku上のChrome Driverを指定(※デプロイするときはコメントを外す)
# driver_path = '/app/.chromedriver/bin/chromedriver'
# Headless Chromeをあらゆる環境で起動させるオプション
options = Options()
options.add_argument('--disable-gpu');
options.add_argument('--disable-extensions');
options.add_argument('--proxy-server="direct://"');
options.add_argument('--proxy-bypass-list=*');
options.add_argument('--start-maximized');
options.add_argument('--headless');
# クローラーの起動
driver = webdriver.Chrome(executable_path=driver_path, chrome_options=options)
# Yahooの天気サイトにアクセス
driver.get('https://weather.yahoo.co.jp/weather/')
# ソースコードを取得
html = driver.page_source
# ブラウザを終了する
driver.quit()
# HTMLをパースする
soup = BeautifulSoup(html, 'lxml') # または、'html.parser'
# スクレイピングした《今日の日本の天気予報の要約》を変数に格納
message = soup.select_one('#condition > p.text').get_text()
# LINEに通知させる関数
def line_notify(message):
line_notify_token = '{{LINE_NOTIFY_TOKEN}}'
line_notify_api = 'https://notify-api.line.me/api/notify'
payload = {'message': message}
headers = {'Authorization': 'Bearer ' + line_notify_token}
requests.post(line_notify_api, data=payload, headers=headers)
# LINEに通知させる
line_notify(message)
$ python main.py
正しく動けば、LINEにピコンと通知が行ってると思います。
ローカルでちゃんとプログラムが動くことを確認したら、次はHerokuで動かすための環境を構築します。
Herokuの環境構築
Heroku CLIをインストールする
Herokuのアカウント登録をすませ、ターミナルからheroku
コマンドを実行できるように、Heroku CLIをダウンロードします。
MacユーザーでHomebrewを入れていれば、brew
コマンドでダウンロードできます。
$ brew tap heroku/brew && brew install heroku
その他のOSの方やbrew
コマンドが使えない方は公式からダウンロードして下さい。
Herokuにログインする
ターミナルからheroku login
コマンドでHerokuにログインします。
$ heroku login
ログインに成功すると下のようなメッセージが表示されます。
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/cli/browser/{{Herokuの番号}}
Logging in... done
Logged in as {{herokuのアカウント}}
ログイン後に表示されるブラウザのタブは閉じても問題ありません。
アプリを作成する
heroku create
コマンドを使用して、HerokuにPythonのプログラムを実行するためのアプリケーションを作成します。
アプリケーション名を指定する方法としない方法の2パターンがあります。指定しない場合、適当な名前が設定されます。
$ heroku create
$ heroku apps:create {{アプリ名}}
アプリ名を指定した場合、他のアプリケーションと名前が被ってはいけません。アプリが作れないエラーが発生します。
Name {{アプリ名}} is already taken
buildpackを追加する
ターミナルでheroku buildpacks:add
コマンドを使用して、3つのbuildpackを追加します。
heroku/python
:HerokuでPythonのプログラムを実行するためheroku-buildpack-google-chrome
:HerokuでHeadless Chromeを使うためheroku-buildpack-chromedriver
:HerokuでChrome Driverを使うため
$ heroku buildpacks:add heroku/python -a {{アプリ名}}
$ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-google-chrome -a {{アプリ名}}
$ heroku buildpacks:add https://github.com/heroku/heroku-buildpack-chromedriver -a {{アプリ名}}
{{アプリ名}}
には、先程heroku create
コマンドで作成したアプリ名を入力します。
3つのbuildpackがちゃんと追加されているかどうか確認する場合は、Herokuの管理画面からSetting
> Buildpacks
で確認できます。
タイムゾーンをAsia/Tokyoに変更する
HerokuのデフォルトのタイムゾーンはUTCなので、Asia/Tokyoに変更します。
$ heroku config:add TZ=Asia/Tokyo -a {{アプリ名}}
成功すると下のようなメッセージが表示されます。
Setting TZ and restarting ⬢ {{ アプリ名 }}... done
TZ: Asia/Tokyo
Herokuにデプロイする
環境変数にトークンをセットする
いま、main.py
の変数line_notify_token
に、直接トークンが入っているので、万が一を備えてHerokuの環境変数を使用します。
line_notify_token = '{{LINE Notify トークン}}'
heroku config:set
コマンドを使い、Herokuの環境変数にトークンの値をセットします。
$ heroku config:set LINE_NOTIFY_TOKEN=**************** -a {{ アプリ名 }}
Herokuの環境変数は、heroku config
コマンドで確認できます。
$ heroku config -a {{ アプリ名 }}
あわせて、main.py
も変更します。os
モジュールをインポートして、os.environ
を使って、環境変数を呼び出せるように書き換えます。
import os
line_notify_token = os.environ['LINE_NOTIFY_TOKEN']
ファイルを用意する
HerokuでPythonのプログラムを動かすために、2つのテキストファイルを用意しなければいけません。
requirements.txt
:サードパーティ製のモジュールを記載するruntime.txt
:Pythonのバージョンを記載する
requirements.txt
pip freeze
コマンドで、仮想環境にインストールされたサードパーティ製のモジュールが表示されます。
$ pip freeze > requirements.txt
┣ bin/
┣ include/
┣ lib/
┣ main.py
┣ pyvenv.cfg
┗ requirements.txt NEW
runtime.txt
仮想環境のPythonのバージョンは、python -V
で確認します。
このとき、HerokuがサポートしているPythonのバージョンよりも新しいバージョンを記入しないようにしましょう。Heroku Python Support
$ echo python-3.x.x > runtime.txt
┣ bin/
┣ include/
┣ lib/
┣ main.py
┣ pyvenv.cfg
┣ requirements.txt
┗ runtime.txt NEW
gitでデプロイする
Herokuにデプロイするためにはgitを使います。
初回時のみ
$ git init
┣ .git/ NEW
┣ bin/
┣ include/
┣ lib/
┣ main.py
┣ pyvenv.cfg
┣ requirements.txt
┗ runtime.txt
$ heroku git:remote -a {{アプリ名}}
$ git add .
$ git commit -m "{{コメント}}"
$ git push heroku master
デプロイに成功すれば次のような文字列が表示されます。
# 中略
remote: Verifying deploy... done.
更新時
$ git add {{変更したファイル名}}
$ git commit -m "{{コメント}}"
$ git push heroku master
動作確認
Heroku上にデプロイしたPythonファイルはheroku run
コマンドで簡単に実行することができます。
$ heroku run python main.py
トラブルシューティング
この章では、筆者が実際に遭遇したエラーとその解決策について備忘録がてら記録していきます。
selenium.common.exceptions.SessionNotCreatedException
Message: session not created: This version of ChromeDriver only supports Chrome version 83
これは、buidpackに追加したGoogleChromeとChromeドライバーのバージョンが違うことで発生したエラーケースです。
herokuのChromeドライバーはデフォルトで、Latest版(次期リリース対応)のものがダウンロードされるので、滅多に起こりませんがダウンロードする時期によって、2つのバージョンがずれることがあります。
# Google Chrome (81.0.4044.138)
$ heroku run google-chrome --version -a {{ アプリ名 }}
# Chrome Driver (83.0.4103.39)
$ heroku run chromedriver -v -a {{ アプリ名 }}
この解決策は、ChromeドライバーのバージョンをGoogleChromeのバージョンに合わせます。
heroku config:set CHROMEDRIVER_VERSION=81.0.4044.138 -a {{ アプリ名 }}
そして、次回のデプロイ時に環境変数に登録されているバージョンのChromeドライバーがダウンロードされます。
プログラムの変更がなく、デプロイだけ行いたいときは空コミットをすると良いです。
# 空コミット
$ git commit --allow-empty -m "allow empty commit"
# デプロイ
$ git push heroku master
Chromeドライバーのバージョンが下がったか確認してみましょう。
# Google Chrome (81.0.4044.138)
$ heroku run google-chrome --version -a {{ アプリ名 }}
# Chrome Driver (81.0.4044.138)
$ heroku run chromedriver -v -a {{ アプリ名 }}
おまけ:その他のHerokuのコマンド
# アプリを削除
$ heroku apps:destroy --app {{{ アプリ名 }} --confirm {{ アプリ名 }}
# 環境変数を確認・セットする
$ heroku config -a {{ アプリ名 }}
$ heroku config:set ACCESS_TOKEN=*********************** -a {{ アプリ名 }}
# アプリ一覧を表示
$ heroku list
# ログを確認
$ heroku logs
# 直近のリリース一覧を表示する
$ heroku releases
# Dynoの使用量確認(freeプランで運用するときに見る)
$ heroku ps
# gitのURLを変更する
$ git remote set-url heroku
Herokuで定期実行する
やっとこの記事の本題に入ります、笑
Herokuで定期実行するための方法は何種類かあるのですが、その中でも割とメジャーな方法を2つだけ紹介します。
その前に、無料有料問わず、定期実行(cron)させたい場合は、クレジットカードの登録が必要不可欠です。先にアカウントページでクレジットカードを登録させましょう。
[無料] heroku scheduler
- 無料
- タスクは複数登録可能
- タスクの起動間隔は10分おき、1時間おき、1日おきの3パターンから選べる。
heroku addons:add
コマンドで、heroku schedulerを追加する$ heroku addons:add scheduler:standard
Create job
ボタンを選択するhttps://dashboard.heroku.com/apps/{{アプリ名}}/scheduler
実行時間を以下の3つの中から選びます
Every 10 minutes
:毎日10分ごとに実行Every hour at…
:毎日1時間ごとに実行Every day at…
:毎日何時に実行
Every day at…
を選んだ場合、HerokuのタイムゾーンがUTCなので、日本時間に合わせて+9時間させます。
つまり、毎日16時に実行させたい場合は、07:00 AM
を選択するという訳です。
Herokuにデプロイしたmain.py
をpython
コマンドで実行させるので、python main.py
を入力します。
これでLINEにピコンと通知が来れば完了です!
無料でお手軽ですが、曜日を指定したり特定の日だけ選んだりして定期実行などは出来ないので、注意が必要です。
[有料] Cron To Go
スクレイピングを曜日を指定したり特定の日だけ選んだりして定期実行したい場合に使うのがCron To Goです。
Unix cron形式と全く同じ設定方法で、スケジュールを定義することができます。
分 時 日 月 曜日 コマンド
* * * * * some_command
スケジュール | 値 |
---|---|
分 | 0 〜59 |
時 | 0 〜23 |
日 | 1 〜31 |
月 | 1 〜12 |
曜日 | 0 〜7 (0 ,7 =日曜、1 =月曜、2 =火曜、3 =水曜、4 =木曜、5 =金曜、6 =土曜) |
1週間の無料トライアル
無料トライアル版には、最大15ジョブ、無制限の実行、チャットサポートなど、シルバープランに関連するすべての機能が含まれています。
試用期間が終了すると、プランをアップグレードするまで設定したジョブが自動的に一時停止されます。勝手に課金されないので、試しに使ってみてはどうでしょうか。
https://elements.heroku.com/addons/crontogo
設定方法
heroku addons:add
コマンドで、Cron To Goを追加する$ heroku addons:create crontogo:free-trial
Add job
ボタンを選択するスケジュールの名前や実行時間、コマンドを入力したらadd job
ボタンを押して完成です。
これでLINEにピコンと通知が来れば完了です!
有料版ということもあり、UI・UXの設計がしっかりしていて抜群に使いやすいアドオンです。
連載:Pythonでクローリング/スクレイピング
《超実践的》Pythonでクローリング/スクレイピングを行うロードマップ
クローリングとスクレイピングをする前の事前準備・知識など- 静的なWebページを『Beautiful Soup』でスクレイピングする
ログインが必要なWebサイトを『Selenium』でクローリングし、『Beautiful Soup』でスクレイピングするJavaScriptで書かれたページを『Selenium』でスクレイピングするWeb APIやデータセットを使用してスクレイピングするスクレイピングで得たデータを様々な形式(pandas、BigQuery、スプレッドシート、DBなど)に変換するクローリング/スクレイピングをローカルマシンで定期実行する方法- クローリング/スクレイピングをサーバーで定期実行する方法
- クローリング/スクレイピングを安定させるための3つの設定(待機処理・エラーの通知・処理のリトライ)
クローリング/スクレイピングの次にやるべきこと
Selenium逆引きリファレンス
- Seleniumチートシート
【headlessモード】ブラウザを起ち上げずにSeleniumを実行する方法アラートダイアログを操作するセレクトボックスを選択する方法Basic認証を突破する方法- 2段階認証(6桁のパスコード)を突破する方法
- reCAPTCHAを突破する方法
- 【target="_blank"対策】driverを別ウィンドウに切り替える方法
【display:none対策】JavaScriptを実行して隠された要素を表示させる方法【表示読み込み対策】時間を遅延させるBeautiful Soupと組み合わせるherokuで定期実行させる手順