東京都が、東京都における新型コロナウイルス(COVID-19)の陽性者数を日々公開しています。
このデータをSlackに自動投稿したいと考え、ソースコードを作成しました。
できたもの
東京都の新型コロナウイルス感染症対策サイトのデータを取得・整理・プロットし、Slackに投稿します。
環境
- 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.py
とplot.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)
↑こちらを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("", "")
の処理です。
↑こちらの画像を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を作成しました。
今後の課題ですが、東京都のデータが更新されたタイミングで自動通知できればなおよいと考えています。
もしよい方法を思いついたらトライしてみたいと思います。