TL;DR
長野県版 非公式 新型コロナウイルス感染症対策サイトのデータ更新(Webスクレイピングしたり再エンコーディングしたりCSVからJSONへマッピングしたり)をGitHub Actionsを使って自動化しました。サイトのリポジトリにはデータファイルの管理をせずにアプリケーション関連のファイルのみを管理をするので開発もスッキリ。
開発したスクリプトはこちら:
これらのスクリプトで作られたJSONファイルを使っているリポジトリはこちら:
自己紹介
昔は高専生でした。悪いことしてなくてもディスられる世の中、怖い・・・
https://twitter.com/IiToshihide/status/1246487047545556992
目次
0. 経緯
1. オープンデータ自動ダウンロード & CSV -> JSON 変換
2. ニュース一覧をスクレイピング & JSON 変換
3. 自動更新されたファイルでサイトを自動ビルド
4. まとめ
5. 謝辞
0. 経緯
東京都とCode for Japanが開発した東京都 新型コロナウイルス感染症対策サイトと、それをforkして各地域のサイトが開発されているのを見て、出身地の長野県版に協力したいと思いcontributeを始めました。
一つ目のスクリプトを書いた後で、長野県版の先行作業されていた有志の方たちがデータを手作業で更新していることを知りました。その方達を助けられるよう、自動化に観点を置いてコードを書いてきました。この知識が他の自治体やプロジェクトの一助になれば、と思います。
1. オープンデータ自動ダウンロード & CSV -> JSON 変換
Contributeするにあたり最初に確認したのは、オープンデータの有無でした。幸いにも、長野県のサイトでCSVファイルを見つけることができました。しかし、これらの公開されていたファイルには以下のようにいくつかの問題がありました。
- 利用するのに難がある。ファイルタイトル(カラムヘッダではない)がついていたり合計行があったりする。
- ファイルエンコードがSHIFT JIS。UNICODEが欲しい。
- ファイル名が毎日変わる。ファイル名のsuffixに日付がつくので、毎日ファイル名が変わる。古い日付のファイルはサーバから削除される。ファイル名が変わることもある(
hasseijoukyou0402.csv
もあればhasseizyokyo0411.csv
もある)。
これらの問題を解決するためにWebスクレイピングしてCSVをJSONに変換するスクリプトを書きました。
- 長野県のページをスクレイピングし、公開されているCSVファイルのURLを取得する。
- ファイルをダウンロードし、エンコード変換したCSVファイルを保存する。
- CSVから不要な行を削除し、JSONにマッピングし、生成されたJSONファイルを保存する。
たまに出現する全角数字(!)を半角数字に変換したり、複数のファイルから data.json
を作成する処理も同スクリプトでやっています。
これを自動で毎時間実行するための GitHub Actions ワークフローを作成しました。
毎時間実行するためには、scheduled event
というEvent Triggerを使います。
...
on:
schedule:
- cron: "0 * * * *"
...
スクリプトを走らせて、新しいファイルが作成されたかを確認します。今回は git status | grep modified
でチェックすれば十分でした。
新しいファイルが作成されていたらそれを git add
& git commit
して同リポジトリに保存します。
...
- name: Commit files
run: |
git config --local user.email "wataru.oguchi.0to1@gmail.com"
git config --local user.name "Wataru Bot"
git status | grep modified && git add src/.encoded src/.json && git commit -v -m "[Bot] GitHub Actions - auto run" || true
...
これによって、同リポジトリに常に最新のCSVファイル及びJSONファイル(data.json
)が自動で保存されるようになりました。(git push
もお忘れなく!)
2. ニュース一覧をスクレイピング & JSON 変換
グラフを表示するには data.json
というファイルを参照している一方で、ニュース一覧には news.json
というファイルを参照しています。県のサイトを見て手作業で更新されている様子でしたので、単純にWeb スクレイピングをし、一つ目のスクリプトと同様にJSONファイルに保存することとしました。Web スクレイピングをするスクリプトはこちら。
こちらも前述したスクリプトと同様、GitHub Actions ワークフローを作成し、毎時間実行しています。
3. 自動更新されたファイルでサイトを自動ビルド
ここまでで必要なJSONの生成は自動化することができました。
実際のページは静的サイトなので、データを更新するためにはビルドする必要があります。ここでのタスクは以下の通りです。
- データが更新された時に再ビルドを行うため、データ生成スクリプト側で通知する。
- 実際にページをビルドするプロジェクトで通知を受け取る。
- 最新のデータをダウンロードして再ビルドを行う。
1. データ更新の通知を送信する
JSONデータを生成する wataruoguchi/covid19_nagano_csv_to_json
や wataruoguchi/covid19_nagano_scraper
側のGitHub Actionsを使います。上記で紹介したGitHub Actions ワークフローのスニペットにGitHub APIへPOSTリクエストを送る curl
コマンドを加えます。
...
- name: Commit files
run: |
git config --local user.email "wataru.oguchi.0to1@gmail.com"
git config --local user.name "Wataru Bot"
git status | grep modified && git add src/.encoded src/.json && git commit -v -m "[Bot] GitHub Actions - auto run" \
&& curl -X POST \
-H "Authorization: Bearer ${{ secrets.ACCESS_TOKEN }}" \
-H "Accept: application/vnd.github.everest-preview+json" \
-H "Content-Type: application/json" \
https://api.github.com/repos/Stop-COVID19-Nagano/covid19/dispatches --data '{"event_type": "build_application"}' \
|| true
...
変更点を抜粋したものが以下のスニペットです。
curl -X POST \
-H "Authorization: Bearer ${{ secrets.ACCESS_TOKEN }}" \
-H "Accept: application/vnd.github.everest-preview+json" \
-H "Content-Type: application/json" \
https://api.github.com/repos/Stop-COVID19-Nagano/covid19/dispatches \
--data '{"event_type": "build_application"}'
GitHub API v3 の dispatch event を使い、Webサイトのリポジトリである Stop-COVID19-Nagano/covid19
に dispatch イベント { "event_type" : "build_application" }
をPOSTしています。 secrets.ACCESS_TOKEN
はrepo権限のみを持ったパーソナルアクセストークンを作成し、そのトークン値を wataruoguchi/covid19_nagano_csv_to_json
リポジトリ側の Secrets
に保存しています。前提として、イベントが送信される側のリポジトリの管理者権限を持っている必要があるかと思います。
2. データ更新の通知を受け取る
データ更新の通知を受信するためにはWebサイトのリポジトリ Stop-COVID19-Nagano/covid19
のGitHub Actions ワークフローを更新します。
イベントトリガーのところに、repository_dispatch
とその types
である build_application
を追加します。
...
on:
repository_dispatch:
types: [build_application]
...
これだけの変更で dispatch event を受け取り次第、このworkflowが実行されるようになります。
注意すべき点は repository_dispatch
は master
リポジトリに無いと動かないということ。ドキュメントにはデフォルトブランチ(私たちの場合はdevelopment
)でも動くと書いてあったのに動作は確認できませんでした。( 軽く2時間は溶けました :/ )
3. 最新のデータをダウンロードしてビルド
最新のデータは全てスクリプトのリポジトリにあるので、ビルドをする前にshellスクリプトで全てのJSONファイルをダウンロードします。
実際のファイルはこちら (bin/cp_data.sh)。
この bin/cp_data.sh
を generate 時に実行するよう、package.json
内の scripts
に変更を加えました。
...
"scripts": {
"cp:data": "./bin/cp_data.sh",
"generate:deploy": "yarn cp:data && yarn generate:deploy:orig",
"generate:deploy:orig": "nuxt-ts generate",
}
...
generate:deploy:orig
が元々 generate:deploy
だったものです。新たにデータをダウンロードするスクリプト./bin/cp_data.sh
を実行するcp:data
を追加し、デフォルトで使われるgenerate:deploy
でこれを実行するよう変更しました。
4. まとめ
以上の変更で、手動で行ってきた以下のタスクが全て自動化されました。
- 県のサイトからCSVファイルを探してダウンロードする。
- CSVファイルをダウンロードしてJSONへ変換する。
- JSONが変わったときに、ページの再ビルドを行う。
同様に、自動化スクリプト側でデータ管理を行うことになり、Webサイトのリポジトリからデータ管理を外れたことで、以下の利益も生まれました。
- 同リポジトリの変更がデータの変更タイミングに依存せず、アプリケーション変更に集中できる。
- データに誤りがあったらデータ生成スクリプトの修正をすればいいだけなので、アプリケーション側でデプロイ等する必要がない。
- 以前同リポジトリで行っていたHTTP GETでの最新JSON取得を廃止し、ビルドのタイミングで行うためレンダリングパフォーマンスが良い。
5. 謝辞
長野県版を立ち上げてくださった方、長野県庁の中の人と連絡を取ってくださった方、長野県のオープンデータを整備されている方、アプリのメンテナンス/改善をしてくださった方、みなさんの力でこのページが生まれて、県内に住む方々から良いフィードバックを得られています。ありがとうございます。
東京都のメインプロジェクトから派生して、長野県だけでなく全国各地で同じようにプロジェクトが生まれています。これらのプロジェクトに時間と能力を費やしている方、Code for JapanのSlack チャンネル #covid19, #stopcovid19jp の方にも知識の提供をいただいています。ありがとうございます。
身近にいる人の感染がわかってからでは身を守るのが手遅れになるかもしれません。
感染を遅らせることができれば、医療機関も一人ひとりの患者の治療に時間をとれます。外出を控え、自分だけでなく、自分の大切な人たち、そしてその人たちの大切な人たちを守ることに協力してください。