[Python]FastAPIとPandasを用いたコロナウイルス情報取得API作成方法 メモ

* Web API構築用フレームワークFastAPIとデータ処理用ライブラリPandasを用いたコロナウィルス情報を取得するAPIの作成方法についてメモする。
  * 都道府県名を指定し、月別の感染者数とワクチン接種（2回）数を取得する



## 事前準備

* `ndjson`インストール※ワクチン接種情報がNDJSON形式で提供されているため。

  ```shell
  pip install ndjson
  ```

* その他、pandas,FastAPI周りも必要に応じインストールすること。

## コード

* 都道府県名を指定し、月別の感染者数とワクチン接種（2回）数を取得する2つのAPIを作成する。



* `main.py`

```python
from fastapi import FastAPI
from pydantic import BaseModel
import pandas as pd
import requests
from urllib.request import Request, urlopen
import ndjson


app = FastAPI()

# 都道府県 コード-名称 対応辞書
prefcode_with_name = {
    1: '北海道', 2: '青森県', 3: '岩手県', 4: '宮城県', 5: '秋田県', 6: '山形県', 7: '福島県', 8: '茨城県', 9: '栃木県', 10: '群馬県', 11: '埼玉県', 12: '千葉県', 13: '東京都', 14: '神奈川県', 15: '新潟県', 16: '富山県', 17: '石川県', 18: '福井県', 19: '山梨県', 20: '長野県', 21: '岐阜県', 22: '静岡県', 23: '愛知県', 24: '三重県', 25: '滋賀県', 26: '京都府', 27: '大阪府', 28: '兵庫県', 29: '奈良県', 30: '和歌山県', 31: '鳥取県', 32: '島根県', 33: '岡山県', 34: '広島県', 35: '山口県', 36: '徳島県', 37: '香川県', 38: '愛媛県', 39: '高知県', 40: '福岡県', 41: '佐賀県', 42: '長崎県', 43: '熊本県', 44: '大分県', 45: '宮崎県', 46: '鹿児島県', 47: '沖縄県'
}


# 指定した都道府県の月別ワクチン摂取情報(2回)を取得する
# 取得元:新型コロナワクチンの接種状況に関するオープンデータ
# https://cio.go.jp/c19vaccine_opendata
def search_covid19_prefectures_monthly_vaccination_data(pref):
    result = requests.get(
        'https://vrs-data.cio.go.jp/vaccination/opendata/latest/prefecture.ndjson')
    # NDJSON形式のデータをDataFrameとして取得
    df = pd.DataFrame(ndjson.loads(result.text))
    # 都道府県名列の追加
    df['都道府県名'] = df.prefecture.map(
        lambda x: prefcode_with_name[int(x)])
    # データ整形(都道府県名、摂取回数でフィルターをかけ、日付と件数列を抽出)
    df = df[(df['都道府県名'] == pref) & (
        df['status'] == 2)].loc[:, ['date', 'count']]
    df["date"] = pd.to_datetime(df["date"])
    # 月別集計
    result = df.resample('M', on='date').sum().to_dict()['count']
    converted_result = {}
    for key in result.keys():
        new_key = str(key.year)+"-"+str(key.month)
        converted_result[new_key] = result[key]
    return converted_result


# 指定した都道府県の月別感染者情報を取得する
# 取得元:新たに確認された感染者数（NHKまとめ）
# https://www3.nhk.or.jp/news/special/coronavirus/data/
def search_covid19_prefectures_monthly_infected_data(pref):
    # Pandas.read_csvでは403となったため、urllibを使用する
    req = Request(
        'https://www3.nhk.or.jp/n-data/opendata/coronavirus/nhk_news_covid19_prefectures_daily_data.csv')
    req.add_header(
        'User-Agent', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0')
    content = urlopen(req)
    df = pd.read_csv(
        content, index_col=0, parse_dates=True)

    # 選択した都道府県データを抽出
    df = df[df['都道府県名'] == pref]
    column_name = '各地の感染者数_1日ごとの発表数'
    df = df.loc[:, [column_name]]

    # 月別合計値を取得
    df = df.resample('M').sum()
    result = df.to_dict()[column_name]
    converted_result = {}
    for key in result.keys():
        new_key = str(key.year)+"-"+str(key.month)
        converted_result[new_key] = result[key]
    print(converted_result)
    return converted_result


# リクエストボディモデル定義
class SearchQuery(BaseModel):
    pref: str


# 都道府県月別 感染者数取得API
@app.post('/search_covid19_prefectures_monthly_infected_data')
def search_covid(searchQuery: SearchQuery):
    # リクエストボディに指定された都道府県名を取得
    pref = searchQuery.pref
    return search_covid19_prefectures_monthly_infected_data(pref)


# 都道府県月別 ワクチン接種者(2回)数取得API
@app.post('/search_covid19_prefectures_monthly_vaccination_data')
def search_covid(searchQuery: SearchQuery):
    # リクエストボディに指定された都道府県名を取得
    pref = searchQuery.pref
    return search_covid19_prefectures_monthly_vaccination_data(pref)

```



### 動作確認

* 起動

  ```shell
  uvicorn main:app --reload
  INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
  INFO:     Started reloader process [1792] using statreload
  INFO:     Started server process [11672]
  INFO:     Waiting for application startup.
  INFO:     Application startup complete.
  ```

* **都道府県月別 感染者数取得API**

  * リクエスト

    ```http
    POST /search_covid19_prefectures_monthly_infected_data HTTP/1.1
    Host: localhost:8000
    Content-Type: application/json
    Content-Length: 22
    
    {
        "pref":"東京都"
    }
    ```

  * レスポンス

    ```json
    {
        "2020-1": 3,
        "2020-2": 34,
        "2020-3": 489,
        "2020-4": 3748,
        "2020-5": 957,
        "2020-6": 994,
        "2020-7": 6464,
        "2020-8": 8125,
        "2020-9": 4918,
        "2020-10": 5350,
        "2020-11": 9861,
        "2020-12": 19369,
        "2021-1": 40367,
        "2021-2": 10997,
        "2021-3": 9310,
        "2021-4": 18090,
        "2021-5": 21879,
        "2021-6": 12979,
        "2021-7": 44034,
        "2021-8": 61164
    }
    ```

* **都道府県月別 ワクチン接種者(2回)数取得API**

  * リクエスト

    ```http
    POST /search_covid19_prefectures_monthly_vaccination_data HTTP/1.1
    Host: localhost:8000
    Content-Type: application/json
    Content-Length: 22
    
    {
        "pref":"東京都"
    }
    ```

  * レスポンス

    ```json
    {
        "2021-5": 70807,
        "2021-6": 1135173,
        "2021-7": 1968301,
        "2021-8": 802756
    }
    ```

    

## 参考情報

* [COVID-19 オープンデータを可視化する③：政府CIOポータルのワクチン接種状況【Python】](https://biotech-lab.org/articles/11229#i-3)
* [新たに確認された感染者数（NHKまとめ）](https://www3.nhk.or.jp/news/special/coronavirus/data/)
* [新型コロナワクチンの接種状況に関するオープンデータ仕様](https://cio.go.jp/c19vaccine_opendata)