Scrapyでイベントサイトの更新情報を定期ツイート on Heroku


目的

以前書いたスクリプトのリプレイス。
以下問題点を解消したかった。

  • 可読性 : フレームワークを使わずにbeautifulsoupで書いてた(beautifulsoupは悪くない)
  • パフォーマンス : 同期通信だからおっそい
  • 制約下の縛りプレイ : Herokuにクレカ登録してなかった(詳細は後述)

クレカは登録したけれど無料で運用する意思は貫いてます。

パッケージ管理

pipenvを使用。
HerokuはPipfileに対応してるため、requirements.txtをわざわざ作成する必要もないです。
参考

インストールしたパッケージ

scrapy

djangoライクなテンプレートを提供してくれるため、
データの定義・取得・バリデーション・保存・アウトプットに関するコードの棲み分けがしやすい。
あと非同期通信。特に意識せず実装できるすごい。
download-delayの設定はお忘れなく

scrapy-selenium

scrapyでselenium使うためのmiddleware
スクレイピングしてて、あれ?取れない??DevToolsでは見れるのにってなった際、
インストールすれば幸せになれるはず。

READMEより

SELENIUM_DRIVER_NAME = 'firefox'
SELENIUM_DRIVER_EXECUTABLE_PATH = which('geckodriver')

chromedriver使うならこう!


SELENIUM_DRIVER_NAME = "chrome"
SELENIUM_DRIVER_EXECUTABLE_PATH = which("chromedriver")

redis

前回取得した記事のidを保管
rdbsを使うまでもないし、IOのパフォーマンスもいいし、Herokuのプラグインもあったので。
クレカ登録しないとプラグインインストールできません
ローカルで動かすときはdockerコンテナ立てました。
こちら参考にさせて頂きました。

twitter

これは前回と同じの。api使うための認証情報とツイート本文を与えるだけでお手軽ツイート

プロジェク作成

$ mkdir project
$ cd project
$ pipenv install scrapy
# プロジェクトのディレクトリをカレントディレクトリに作成
$ pipenv run scrapy startproject project .

その後の具体的な実装はGitHubで。
スクレイピングするurlは環境変数で与えるようにしてます。

構成はこんな感じ。

event_notify #プロジェクトルート
├── event_notify
│   ├── items.py # 取得項目の定義
│   ├── pipelines.py # 取得項目のバリデーション〜アウトプット
│   ├── settings.py # 各種設定
│   └── spiders
│       └── eventsite.py # スクレイピング
├── example.env #環境変数(サンプル)

簡単な流れ

  1. イベント情報サイトの検索結果を取得
  2. 次のページがある場合はそちらも再帰的に取得
  3. 各イベントのページもスクレイピングして、タイトル・url・開催場所・値段・販売状況等取得
  4. 取得した情報に空のフィールドがあったら捨てる
  5. redisから前回取得したイベントのリストを取得して変数に保持(実行時1度のみ)
  6. redisを空に(実行時1度のみ)
  7. redisに今回取得イベントを追加
  8. if 今回取得イベント in 変数 : 捨てる
  9. 捨てられなかったイベントはツイート

Herokuにデプロイ

bitbucketではこんな感じでやってましたが、Githubではめっちゃ簡単に出来ました。
マニュアルを見るまでもなく、HerokuのGUIからポチポチーで秒で出来ます。
GitHubのmasterにpushされたらherokuのmasterにpushってよくあるパターン。

Heroku アドオン

定期実行

Heroku Scheduler
無料プランだと、実行間隔を10分・1時間・1日の3択からしか選べない

redis

heroku-redisを使用。無料枠あり。
GUIからデータは見れないので、CLIからredisのコマンド叩いて確認。

ログ管理

Papertrail
概要はこちらが参考になるかと。
scrapyのログレベルをデフォルト(DEBUG)のままにしてると、
フリープランの場合すぐに限界量(10MB/1day)に達してしまうため、INFOに変更推奨。
あと、redisのヘルスチェックもPapertrail側のフィルターで無視しておくと良きかと。

settings.py

LOG_LEVEL = "INFO"

chromedriver

chromedriverをHeroku上でも使うため下記インストール
- https://github.com/heroku/heroku-buildpack-google-chrome.git
- https://github.com/heroku/heroku-buildpack-chromedriver.git

VSCodeでのデバッグ

構成の追加ボタンからScrapy用のテンプレートを追加すれば簡単にデバッグ可能。
これ地味にすごいありがたい

無料枠について

クレカ登録すると無料枠が拡張されるのと、各プラグインがインストールできるようになります。
プラグインには無料プランがある(ものもある)ので普通に無料で運用できます。
前回紹介したプラグインを使わないタスクスケジューリング方法では、
結局タスクを実行させるcron的なプロセスは動いてるので無料枠が消費されました。
クレカ登録してないので課金は絶対されないけれど。

今回紹介したスケジューラプラグインの場合、
実行中しか枠を消費しないので、拡張された無料枠が食い尽くされることはないです。
というかdyno1つが24時間稼働しても課金枠には及ばなかったような(詳細は調べてください)

感想

今までbeautifulsoupでゴリゴリ書いてたけど
scrapyで書き直したらほんと見通しがよくなりました。
jupyterでさらっと書きたいとき以外はscrapy使ってこうと思いますた。ほんとおすすめ。