5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

東京都の新型コロナウイルス陽性者数をSlackに投稿するbotをつくる

Last updated at Posted at 2020-04-25

東京都が、東京都における新型コロナウイルス(COVID-19)の陽性者数を日々公開しています。
このデータをSlackに自動投稿したいと考え、ソースコードを作成しました。

できたもの

東京都の新型コロナウイルス感染症対策サイトのデータを取得・整理・プロットし、Slackに投稿します。
c19bot.PNG

環境

  • Win10 bash on windows
  • Python 3.5.2

SlackbotのTokenをあらかじめ用意しておく必要がありますが、本記事ではTokenの発行方法については省略します。

投稿方法

概要

python3 run.pyを実行することで、botがコンテンツをSlackへ投稿するように実装しました。

run.pyの中身はこのようになっています。

import plot
import post

def main():
    df = plot.fetch_csv() # データ取得
    plot.plot_hist(df) # グラフ描画
    txt = plot.info_str(df) # 最新の陽性者数カウント
    post.post_message(txt) # 最新の陽性者数を投稿
    post.upload_figure("", "") # グラフを投稿
 
if __name__ == "__main__":
    main()

run.pyと同じディレクトリにpost.pyplot.pyを用意しました。

データ取得

plot.fetch_csv()の処理です。
東京都の公開データを取得しています。

import requests
import pandas as pd

URL = "https://stopcovid19.metro.tokyo.lg.jp/"
def fetch_csv():
    r = requests.get(URL+"data/130001_tokyo_covid19_patients.csv")
    df = pd.read_csv(io.BytesIO(r.content),sep=",")
    return df

グラフ描画(pandas, matplotlib)

plot.plot_hist(df)の処理です。
取得したデータを用いてpandasでヒストグラムを描画します。

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

PNG = "hist"

def plot_hist(df):
    # 日付列のみをTimestampで抽出
    df = pd.to_datetime(df.loc[:, "公表_年月日"])
    # bins(トータルの日数)を計算
    num_bins = calc_num_days(df)
    # 上下に分割
    fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True)
    df.hist(ax=axes[0], bins=num_bins)
    axes[0].set_title("日別", fontproperties=FP)
    df.hist(ax=axes[1], bins=num_bins, cumulative=True)
    plt.title("累積", fontproperties=FP)
    plt.suptitle("東京都 COVID-19 陽性者数(データ元 "+URL+")", fontproperties=FP)
    # x軸の刻み幅を1週間に設定
    plt.gca().get_xaxis().set_major_formatter(mdates.DateFormatter("%y/%m/%d"))
    plt.gca().get_xaxis().set_major_locator(mdates.DayLocator(interval=7))
    plt.xticks(rotation=90) # x軸ラベルを90度回転
    plt.savefig(PNG, bbox_inches="tight")

calc_num_days(df)で、ヒストグラムのbinsを計算しています。
取得データ(csv)で、ヘッダー行を除いて1行目が初めて陽性者数を確認した日の患者データ、末尾行が最新の患者データであるという前提で書いています。

def calc_num_days(df):
    s = df.iloc[0] # 1行目の1列目
    e = df.iloc[-1] # 末尾行目の1列目
    delta_days = e - s
    return delta_days.days + 1

グラフのタイトルに日本語フォントを使うため、plt.title("累積", fontproperties=FP)のようにfontpropertiesを指定しています。

FONT_TTF = "/home/[ユーザ名]/.local/lib/python3.5/site-packages/matplotlib/mpl-data/fonts/ttf/ipaexg.ttf"
FP = FontProperties(fname=FONT_TTF)

環境によってはわざわざfontpropertiesを指定せずとも日本語フォントが使用できると思います。

ちなみに、元々はヒストグラムでなく棒グラフで描画したいと考えていましたが、うまくいきませんでした。
うまくいかなかった点は、横軸を時間軸でなくラベルとして扱ってしまい、陽性者数が0の日がうまく描画できなかったことです。
陽性者数が0の日のラベルを作ればよいかと考えたのですが、それよりもヒストグラムとして描画するほうが楽かなと考え、ヒストグラムで描画しました。
よりスマートな方法があれば教えていただけると嬉しいです。

最新の陽性者数カウント

plot.info_str(df)での処理です。
データからSlackへ投稿したい文面を作成しています。


def info_str(df):
    # 日付列のみを抽出
    df = df.loc[:, "公表_年月日"]
    today = df.iloc[-1] # 末尾行目の1列目
    # index: 日付, colums: 人数
    df = df.value_counts().sort_index()
    num_y = int(df.iloc[-2]) # yesterday
    num_t = int(df.iloc[-1]) # today
    txt = today+"時点の陽性者:"+str(num_t)+"\n" \
           +"(昨日比:"+str(num_t - num_y)+"人, 計"+str(df.sum())+"人)\n" \
           + URL
    return txt

最新の陽性者数を投稿(Slackbot)

post.post_message(txt)の処理です。
c19bot_u.PNG

↑こちらをSlackに投稿します。

PythonのslackbotモジュールとSlack APIを使用します。


import json
import requests

SLACK_API = "https://slack.com/api/"
TITLE = "COVID-19 東京都の陽性者数"

def post_message(txt, title=TITLE):
    API_METHOD = "chat.postMessage"
    response = requests.post(SLACK_API + API_METHOD, data={
        "token": SLACK_TOKEN,
        "channel": SLACK_CHANNEL,
        "username": USER_NAME,
        "as_user": True,
        "attachments": json.dumps([
            {
                "fallback": title,
                "color":  "#00FFFF",
                "title": title,
                "text": txt,
            }
        ])
    }).json()

SLACK_TOKENにはSlackbotのtokenを記述します。
SLACK_CHANNELにはチャンネル名("#hogehuga")を記述します。
USER_NAMEにはbotのユーザ名を記述します。

グラフ投稿

post.upload_figure("", "")の処理です。
c19bot_b.PNG

↑こちらの画像をSlackに投稿します。

PNG = "hist"
PNG_FILE = PNG+".png"

def upload_figure(txt, title=TITLE):
    files = {"file": open(PNG_FILE, "rb")}
    param = {
        "token": SLACK_TOKEN,
        "channels": CHANNEL_ID,
        "filename": PNG_FILE,
        "initial_comment": title+"\n"+txt,
        "title": title
        }
    requests.post(url=SLACK_API+"files.upload", params=param, files=files)

CHANNEL_IDは、チャンネルのURL末尾の文字列と同じものです。チャンネル名ではありません。

毎日自動投稿するには(crontab)

常時起動している端末(サーバー)であれば、crontabを利用することでコマンドの定期実行が可能です。
例えば毎日15時05分に自動投稿したい場合、crontab -eでエディタを開き、以下を記述します。

05 15 * * * cd [ソースコードがあるディレクトリ]; python3 run.py >run.log

まとめ

東京都における新型コロナウイルスの陽性者数を取得し、Slackに投稿するbotを作成しました。

今後の課題ですが、東京都のデータが更新されたタイミングで自動通知できればなおよいと考えています。
もしよい方法を思いついたらトライしてみたいと思います。

5
2
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
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?