COVID-19Hokkaidoデータ編①スクレイピングなどによる初期データ作成

## もくじ
[COVID-19Hokkaidoデータ編①スクレイピングなどによる初期データ作成](https://qiita.com/Kanahiro/items/b47984fbf77f4e632663)←この記事だよ！
[COVID-19Hokkaidoデータ編②オープンデータ化＋自動更新](https://qiita.com/Kanahiro/items/8f0768708d2c47ae3206)
[COVID-19Hokkaidoデータ編③完全自動化](https://qiita.com/Kanahiro/items/782cb6d5497d3c3704ec)

![スクリーンショット 2020-03-10 17.25.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/db6e9de7-3071-9836-9aba-be5d04c240d3.png)

## はじめに
東京都とCode for Japanにより[東京都公式コロナウイルス対策サイト](https://stopcovid19.metro.tokyo.lg.jp/)がリリースされ、そのソースコードがなんとGitHubでMITライセンスで公開されました。つまり他の道府県でも「同様のデータを用意できれば」同じように可視化するウェブアプリケーションを作成出来るという事です（実際はサーバーのリソースなども必要になってきますが）。画期的な取組です。

さてそんな訳で、全国各地の動きも活発化し、北海道ではCode for Sapporoや道内IT企業、自治体職員など多彩なバックグラウンドをもつ有志による[JUST道IT](https://twitter.com/search?q=JUST%E9%81%93IT)というグループが結成され、3月6日に東京都からフォークしたプロジェクトが3月9日の昼にリリースされました。

[Covid19Hokkaido 北海道新型コロナウイルスまとめサイト](https://stopcovid19.hokkaido.dev/)

ほぼ同時に[神奈川でも同様の形でサイト](https://stopcovid19.kanagawa.work/)がリリースされたり、これらの動きが[窓の杜](https://forest.watch.impress.co.jp/docs/news/1239825.html)さんや[ITmedia](https://www.itmedia.co.jp/news/articles/2003/10/news085.html)さんにも紹介されるなど、東京発の画期的な取組が全国へ波及し始めています。

## この記事について
前述のとおり、「同様のデータを用意できれば」同じように可視化するウェブアプリケーションを作成出来る訳ですが、自治体によってデータの公開状況・提供方法が異なるため、それらをうまいこと収集してデータをこさえるのが重要になってくる訳です。私も微力ながら本プロジェクトに参加させて頂き、データ周りの開発に携わった事から、データの取得・生成・管理のフローについて本記事にまとめる事とします。

## 東京都公式サイトのデータ
東京都公式サイト（以下都サイト）のソースコードでは、data/data.jsonというファイルに可視化されるべきデータが全て格納されており、key別に以下の構成になっていました（フォーク時点）。

| key | 東京都サイトでの名称 | 内容 |
|:--|:--|:--|
| contacts | 新型コロナコールセンター相談件数 | 日別コールセンター相談件数 |
| discharges | 未実装 | 退院者別属性 |
| discharges_summary | 未実装 | 日別退院者数 |
| inspections | 未実装 | 日別検査データ |
| inspections_summary | 未実装（devでは検査実施日別状況） | 検査により陽性が判明した日・人数のデータ（1/24〜、都内外別） |
| patients | 陽性患者の属性 | 感染者別属性 |
| patients_summary | 陽性患者数 | 日別感染者数 |
| better_patients_summary | 未実装 | 感染者数、退院者数、死亡者数、軽症、中等症、重症の日別データ |
| querents | 帰国者・接触者電話相談センター相談件数 | 日別電話相談センター相談件数 |

またkeyごとの各カラムの意味は以下のとおりです（必要なデータのみ）。

| key | colum | 東京版の何に使われているか | memo |
|:--|:--|:--|:--|
| contacts | 日付 | 新型コロナコールセンター相談件数 | utils/formatGraph.tsに読まれます |
| contacts | 小計 | 新型コロナコールセンター相談件数 | utils/formatGraph.tsに読まれます |
| inspections_summary | labels | 検査実施日別状況 | index.vueで読まれます |
| inspections_summary | 都内 | 検査実施日別状況 | index.vueで読まれます |
| inspections_summary | その他 | 検査実施日別状況 | index.vueで読まれます |
| patients | リリース日 | 陽性患者の属性 | utils/formatTable.tsに読まれます |
| patients | 居住地 | 陽性患者の属性 | utils/formatTable.tsに読まれます |
| patients | 年代 | 陽性患者の属性 | utils/formatTable.tsに読まれます |
| patients | 性別 | 陽性患者の属性 | utils/formatTable.tsに読まれます |
| patients_summary | 日付 | 陽性患者数 | utils/formatGraph.tsに読まれます |
| patients_summary | 小計 | 陽性患者数 | utils/formatGraph.tsに読まれます |
| querents | 日付 | 帰国者・接触者電話相談センター相談件数 | utils/formatGraph.tsに読まれます |
| querents | 小計 | 帰国者・接触者電話相談センター相談件数 | utils/formatGraph.tsに読まれます |

以上を踏まえたdata.jsonの構造は以下です。

```javascript:data.json
{
    "contacts": {    
        "data": [
            {
                "日付": "2020-02-14T08:00:00.000Z",
                "曜日": "金",
                "9-13時": "",
                "13-17時": "",
                "17-21時": "",
                "date": "2020-02-14",
                "w": "",
                "short_date": "02/14",
                "小計": 172
            },
            {
                "日付": "2020-02-15T08:00:00.000Z",
                "曜日": "土",
                "9-13時": "",
                "13-17時": "",
                "17-21時": "",
                "date": "2020-02-15",
                "w": "",
                "short_date": "02/15",
                "小計": 179
            },
            //以下略
        ],
        "date": "2020-03-07T17:55:16.974293+09:00"
    },
    "querents": {},
    "patients": {},
    "patients_summary": {}, 
    "discharges": {},
    "discharges_summary": {},
    "inspections": {},
    "inspections_summary": {},
    "better_patients_summary": {},
    "last_update": "2020-03-07T17:55:16.974293+09:00", //ISO-8601フォーマットのdatetime
    "main_summary": {}
}
//last_updateとmain_summary以外は、contactsと同様にdateとdataを持ちます
//contacts以外は要素を割愛しています
//main_summaryは特殊ですが、システム上は使用されていないので無視しても問題ありません
```

## 北海道版V0のデータ
V0時点で外部から取得可能なデータは[北海道のウェブサイト](http://www.pref.hokkaido.lg.jp/ss/tkk/singatakoronahaien.htm)に掲載されているデータに限られており、その中で機械判読可能な情報は、HTMLのtableに表示される[患者の属性](http://www.pref.hokkaido.lg.jp/hf/kth/kak/hasseijoukyou.htm)のみでした。また、札幌市が相談件数のCSVファイルをオープンデータとして公開していた事から、北海道版V0時点ではこれらのデータを用いる事となります。使えるデータと形式は以下のとおりです。

| key | データの出所 | 形式 |
|:--|:--|:--|
| patients | 道ウェブサイト | HTMLのテーブル |
| patients_summary | 道ウェブサイト | HTMLのテーブル |
| contacts | 札幌市 | CSVファイル |
| querents | 札幌市 | CSVファイル |

※検査データはV0では用意出来ないと判断し、表示しない事としました

以上から、V0時点とV1以降の稼働イメージが以下のとおりになりました。
![スクリーンショット 2020-03-10 17.37.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/cfee968c-cffb-cc29-67c0-76b076fa30cd.png)
以下、データの取得・生成・管理のフローをまとめます。

## データの取得・生成
さすがに手作業でデータを更新する事はなく、データ取得・生成を自動で行うスクリプトを書きました。
[Kanahiro/covid19hokkaido_scraping](https://github.com/Kanahiro/covid19hokkaido_scraping/tree/f1fcf80b39ca006c5d028d195619f79c1cdd90d6)
※上記はV0リリース時の状態のURLです（履歴から復元）
※現在は[codeforsapporoにフォークされ](https://github.com/codeforsapporo/covid19hokkaido_scraping)そちらが本流になっております

本スクリプト(main.py)は実行されると以下の手順でdata.jsonを生成します(V0時点)。

1. data.jsonに準拠した構造のdictを生成（各要素のデータは空）
2. 道ウェブサイトをスクレイピングしてdictに変換
3. importの中にあるcsvファイルをすべてdictに変換
4. 2,3のdictを1の各要素に突っ込む
5. 1をdata.jsonとして出力

#### 道ウェブサイトのスクレイピング
道ウェブサイトは、PythonとBeautifulSoupによりスクレイピングしています。table内の全ての行(tr)を取得してdictに変換しています。
※コードは割愛、上記リポジトリのpatients.pyになります

#### CSVファイルの読み込み
V0時点で、contantsとquerentsはソースが生のCSVファイルであるため、importディレクトリ内に保存されたCSVファイルを全て読み込みdictに変換する仕組みとしました。

## データの管理
V0から現在まで、本スクリプトで生成されたdata.jsonを「手動で」本体リポジトリにプッシュしています。
data.jsonの生成自体はGitHub Actionsでスケジューリングしており、15分間隔でmain.pyを実行する事で生成・別ブランチにプッシュするようになっています。
なので、リリース時点では適宜私がdata.jsonを本体にプッシュしていました（1日2,3回くらい）。

## 自動化へ向けて
V0段階ではdata.jsonのプッシュが手動になっており、手間や即時性を考えると自動化したい訳です。
APIサーバーを別途立てるなど、様々な手法が考えられますが、以下であればGitHubのみで完結します。
（都公式も、他自治体での運用も考えてGitHubだけで完結するように作っていたのかなぁ）

- data.jsonの生成をGitHub Actionsで自動化
- その自動生成されたjsonファイルに直接アクセス・非同期通信し、UIに反映させる

※都公式サイトのUIは（フォーク時点では）非同期通信は想定されていなかったので、適宜フロントの修正が必要です