LoginSignup
0
0

More than 3 years have passed since last update.

厚生労働省オープンデータからCOVID-19 PCR検査陽性者数をツィートするDockerコンテナを作ってみた

Last updated at Posted at 2021-04-17

この記事の目的

厚生労働省が公開しているオープンデータをフェッチして、今日のPCR
PCR検査陽性者数をTwitterにツィートするDockerコンテナを作成します。

開発・実行環境

  • Windows ホストPC
    • エディション:Windows 10 Home
    • バージョン:20H2
    • OS ビルド:19042.867
    • エクスペリエンス:Windows Feature Experience Pack 120.2212.551.0
  • Docker Desktop
    • 3.2.2
  • Docker
    • 20.10.5 build 55c4c88

厚生労働省のオープンデータをフェッチする

データをフェッチするスクリプトを開発する

厚生労働省のオープンデータの保存場所を調査します。このページを眺めるとオープンデータは"https://www.mhlw.go.jp/content/"にcsv形式で保存されていることがわかります。

Pythonのrequestsモジュールを使ってオープンデータをフェッチしましょう。今回はこんなコードを書きました。

mhlw/uri_list.py
# coding: UTF-8

uri_list = [
    'pcr_positive_daily.csv',
    'pcr_tested_daily.csv',
    'cases_total.csv'
]

if __name__ == '__main__':
    from sys import stdout

    for uri in uri_list:
        stdout.write(f'{uri}\n')

オープンデータはもっとあるのですが、今回はuri_listで格納した3ファイルをフェッチします。実際に使うのはpcr_positive_daily.csvだけです。

mhlw/fetch.py
# coding: UTF-8

if __name__ == '__main__':
    from sys import argv, path
    from urllib.parse import urljoin

    path.append('http')

    import http_client

    endpoint_url = argv[1]
    uri_list = argv[2:]

    for uri in uri_list:
        url = urljoin(endpoint_url, uri)
        http = http_client.HttpClient(url)
        res = http.sync_get()

        with open(uri, 'w') as f:
            f.write(res)
http/http_client.py
# coding: UTF-8

class HttpClient:
    def __init__(self, url):
        self._url = url

    def sync_get(self):
        import requests

        self._res = requests.get(self._url)

        body = None
        if self._res.status_code == 200:
            body = self._res.text
        else:
            raise RuntimeError('The http request is failed.')

        return body

次のように実行しました。lsコマンドを実行すると3つのcsvファイルがフェッチできていることがわかります。

(base) root@251c769e696d:/mnt/docker/anaconda3/covid-19_mhlw_open_data# python mhlw/uri_list.py | xargs python mhlw/fetch.py "https://www.mhlw.go.jp/content/"
(base) root@251c769e696d:/mnt/docker/anaconda3/covid-19_mhlw_open_data# ls
http  mhlw  pcr_positive_daily.csv  pcr_tested_daily.csv

もともとのcsvの文字コードがutf-8ではないため(たぶんShift-JISかな)、ヘッダの日本語が文字化けしちゃいました。今回は気にせずこのまま進めます。

実行するDockerfileを作成する

次に開発したスクリプトを動作させるDockerコンテナを作ります。

Dockerfile
FROM python:3.9.4-slim-buster

ENV WORKDIR_NAME "/app"
ENV DIR_HTTP "http"
ENV DIR_MHLW "mhlw"
ENV ENDPOINT_URL "https://www.mhlw.go.jp/content/"

WORKDIR $WORKDIR_NAME

RUN mkdir $DIR_HTTP
RUN mkdir $DIR_MHLW

COPY ./${DIR_HTTP}/*.py ${WORKDIR_NAME}/${DIR_HTTP}/
COPY ./${DIR_MHLW}/*.py ${WORKDIR_NAME}/${DIR_MHLW}/

RUN pip install requests

CMD python ${DIR_MHLW}/uri_list.py | xargs python ${DIR_MHLW}/fetch.py ${EHLW_ENDPOINT_URL}

ビルドして実行します。今回は期待値が得られているか確認したいので、次のコマンドでイメージのビルド、コンテナの作成・実行を行います。

PS > docker build --tag=covid19 .
PS > docker run covid19

残念ながらこのままだとPythonスクリプトは期待通りに動作しません。なぜならDockerコンテナからエンドポイントのドメイン mhlw.go.jp の名前解決ができないからです。この辺はDockerコンテナ・ネットワークを解説しないとうまく説明できないので別記事で解説しようと思いますが、一言で説明するとDockerコンテナが属しているネットワーク・サブネットとホストPCが属しているネットワーク・サブネットが異なります。なのでコンテナが属しているネットワークからインターネットに直接アクセスできません。

余談ですが、デバッグしたい場合はコンテナにインタラクティブモードで接続するのがおススメです。

PS > docker run --interactive --tty covid19 bash

コンテナからホスト経由でインターネットにアクセスする

真面目に対応するのであれば、ホストPCのNATの設定を変更したりするのでしょうが(すみません、ちゃんと調査していません)、今回は開発環境兼実行環境のWindowsホストPCで実行するので、Dockerコンテナをhostネットワークに紐づけて実行します。

こんな感じで実行しました。

PS > docker run --net host covid19
cases_total.csv
fetch.py
pcr_positive_daily.csv
pcr_tested_daily.csv
uri_list.py
PS >

hostネットワークって何ぞや、ということですが、簡単に説明しますとdockerをインストールするとデフォルトで3つの仮想ネットワークを作成します。そのうちの1つがhostネットワークでDockerエンジンが動作しているホストPCと同じサブネットの仮想ネットワークにコンテナが接続されます。Dockerの仮想ネットワークはdocker network lsコマンドで確認できます。

PS > docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
e9f868af4a34   bridge    bridge    local
4b2fd62d429a   host      host      local
b475f904ff07   none      null      local

詳しくは公式ページをご覧ください。僕もそのうち解説します。(3年くらいにすごくまじめに調べた記憶があるのですが、今はうるおぼえ故に。すみません。)

補足

オープンデータのフェッチのためにPythonスクリプト書いたけれども、curlでもよかったなぁ。

今日のPCR検査陽性者数を求める

PCR検査陽性者数を分析するスクリプトを開発する

まずは分析のスタートポイントを実装します。

mhlw/analysis.py
# coding: UTF-8

scripts = {
    'pcr_positive_daily.csv': 'analysis_pcr_positive_daily.py',
    'pcr_tested_daily.csv' : None,
    'cases_total.csv' : None
}

if __name__ == '__main__':
    from sys import argv, path
    from os.path import dirname, join
    from subprocess import Popen, PIPE

    import binascii

    file_name = argv[1]
    script = scripts.get(file_name)

    if script != None:
        data = None
        with open(argv[1], 'rb') as input:
            script = join(dirname(argv[0]), script)
            with Popen(['python', script], stdin=input, stdout=PIPE) as pipe:
                data = pipe.stdout.read()

        res = data.decode(encoding='utf-8')
        print(f'\"{res}\"')

次にpcr_positive_daily.csvを解析して、本日のPCR検査陽性者数を分析するスクリプトを作成します。Pandaを使えば簡単に詳細な解析できると思いますが、今回は割愛します。(Pandaはちょっと触ったぐらいなので今後調査したいです。)

analysis_pcr_positive_daily.py
# coding: UTF-8

if __name__ == '__main__':
    from sys import argv, path
    from sys import stdin, stdout
    from csv import reader
    from datetime import datetime

    data = list(reader(stdin))
    header = data[:1][0]
    body = data[1:]

    now = datetime.now()
    key = f'{now.year}/{now.month}/{now.day}'

    item = [ item for item in body if item[0] == key ]

    stdout.write(f'As of {key}, the number of positive PCR tests in Japan is {item[0][1]}.')

開発環境で実行します。こんな感じです。

(base) root@251c769e696d:/mnt/docker/anaconda3/covid-19_mhlw_open_data# ls -1 *.csv | xargs -l python mhlw/analysis.py
"As of 2021/4/15, the number of positive PCR tests in Japan is 4570."
(base) root@251c769e696d:/mnt/docker/anaconda3/covid-19_mhlw_open_data#

Dockerfileを更新する

開発環境で動作したのでDockerfileを更新して動作確認をします。

FROM python:3.9.4-slim-buster

ENV WORKDIR_NAME "/app"
ENV DIR_HTTP "http"
ENV DIR_MHLW "mhlw"
ENV ENDPOINT_URL "https://www.mhlw.go.jp/content/"

WORKDIR $WORKDIR_NAME

RUN mkdir $DIR_HTTP
RUN mkdir $DIR_MHLW

COPY ./${DIR_HTTP}/*.py ${WORKDIR_NAME}/${DIR_HTTP}/
COPY ./${DIR_MHLW}/*.py ${WORKDIR_NAME}/${DIR_MHLW}/

RUN pip install requests

CMD python ${DIR_MHLW}/uri_list.py | xargs python ${DIR_MHLW}/fetch.py ${EHLW_ENDPOINT_URL} && ls *.csv | xargs -l python ${DIR_MHLW}/analysis.py

CMDが長くなってきましたね。シェルスクリプトにまとめたほうが良いかもしれません。イメージからコンテナを作成して実行します。

PS > docker build --tag=covid19 .
PS > docker run --net host covid19
"As of 2021/4/15, the number of positive PCR tests in Japan is 4570."
PS >

ツィートする

本日の陽性者数が分析できたので最後にツィートします。Python Twitter Toolsを使います。

pipでインストールできるようです。

# pip install twitter

今回はこんなコードを書きました。

twitter/twitter_client.py
# coding: UTF-8

if __name__ == '__main__':
    from sys import argv, path
    from datetime import datetime
    from requests_oauthlib import OAuth1Session
    from twitter import *

    path.append('.')
    import runtime_env

    tweet = (argv[1]).replace('"', '')

    endpoint_url    = runtime_env.twitter_endpoint_url()
    token           = runtime_env.twitter_access_token()
    token_secret    = runtime_env.twitter_access_token_secret()
    consumer_key    = runtime_env.twitter_api_key()
    consumer_secret = runtime_env.twitter_api_secret_key()

    t = Twitter(auth=OAuth(token, token_secret, consumer_key, consumer_secret))
    t.statuses.update(status=tweet)

Twitterにツィートするにはアカウントのトークンが必要になりますが、スクリプトにコーディングするのは危険なので環境変数から読み込むようにしています。環境変数を読みだすスクリプトを記述しました。

runtime_env.py
# coding: UTF-8

def twitter_endpoint_url():
    import os
    return os.environ['TWITTER_POST_ENDPOINT_URL']

def twitter_access_token():
    import os
    return os.environ['TWITTER_ACCESS_TOKEN']

def twitter_access_token_secret():
    import os
    return os.environ['TWITTER_TOKEN_SECRET']

def twitter_api_key():
    import os
    return os.environ['TWITTER_API_KEY']

def twitter_api_secret_key():
    import os
    return os.environ['TWITTER_API_SECRET_KEY']

Dockerfileを更新します。こんな感じで記述しました。

FROM python:3.9.4-slim-buster

ENV WORKDIR_NAME "/app"
ENV DIR_HTTP "http"
ENV DIR_MHLW "mhlw"
ENV DIR_TWITTER "twitter"

ENV EHLW_ENDPOINT_URL "https://www.mhlw.go.jp/content/"
ENV TWITTER_POST_ENDPOINT_URL "https://api.twitter.com/1.1/statuses/update.json"

ENV TWITTER_ACCESS_TOKEN "<アクセストークン>"
ENV TWITTER_TOKEN_SECRET "<アクセストークン・シークレット>"
ENV TWITTER_API_KEY "<APIキー>"
ENV TWITTER_API_SECRET_KEY "<APIキー・シークレット>"

WORKDIR $WORKDIR_NAME

RUN mkdir $DIR_HTTP
RUN mkdir $DIR_MHLW
RUN mkdir $DIR_TWITTER

COPY ./*.py ${WORKDIR_NAME}/
COPY ./${DIR_HTTP}/*.py ${WORKDIR_NAME}/${DIR_HTTP}/
COPY ./${DIR_MHLW}/*.py ${WORKDIR_NAME}/${DIR_MHLW}/
COPY ./${DIR_TWITTER}/*.py ${WORKDIR_NAME}/${DIR_TWITTER}/

RUN pip install requests
RUN pip install twitter
RUN pip install requests-oauthlib

CMD python ${DIR_MHLW}/uri_list.py | xargs python ${DIR_MHLW}/fetch.py ${EHLW_ENDPOINT_URL} && ls *.csv | xargs -l python ${DIR_MHLW}/analysis.py | xargs -0 python ${DIR_TWITTER}/twitter_client.py

イマージからコンテナを作成して実行します。こんな感じです。

PS > docker build --tag=covid19 .
PS > docker run --net host covid19
PS >

Twitterのタイムラインを見るとツィートできていました。

2021_04_17_17_37_34.PNG

まとめ

結構おおらかなスクリプトになってしまったので真面目に作るならもうちょいちゃんとしなきゃな、と思います。DockerfileCMD 行が長くなってしまったのでシェルスクリプトかPythonスクリプト化してもよいかもですね。

やっぱり厚生労働省の csv ファイルは curl 使えばよかったと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0