都内のコロナ感染者数を指定時刻に報告してくれるDiscordのBotをDocker上で作成してみた

2020年の新卒でIT系に勤めています。Qiitaでの初投稿なので至らない点もあると思いますがよろしくお願いします。

# 目的
discordApiを用いたbot作りにはまったため、スクレイピングの練習もかねてタイトルのようなbotを作成してみた。

## 実行環境
### Windows
- windows10
- Python3.7.3

### VPS
- CentOS 7

## 手順
### Windows上で構築し、botの動作を確認する
botのトークン取得などは他の記事でたくさん書かれているので省略します。以下のコマンドで使用するライブラリをインストール
`pip install beautifulsoup4`
`pip install requests`
`pip install discord`
今回スクレイピングの対象とするサイトは、[東京都の新型コロナウイルス感染症対策サイト](https://stopcovid19.metro.tokyo.lg.jp/cards/number-of-confirmed-cases/)です。
まずは、BeautifulSoupでほしいデータが取れるか検証してみます。ソースコードは以下のよう。

```python:soup.py

import requests
from bs4 import BeautifulSoup

#getでURLに接続しデータを取得
res= requests.get("https://stopcovid19.metro.tokyo.lg.jp/cards/number-of-confirmed-cases/")

#オブジェクトを格納
soup = BeautifulSoup(res.text,"html.parser")

#ほしい部分のタグおよびクラスでフィルターをかける、extractを使っていらない部分の除去
con = soup.find("span",class_="DataView-DataInfo-summary")
con=soup.find("small",class_="DataView-DataInfo-summary-unit").extract()

text=con.get_text()

text= text.strip()


print(text+"人です")
```
これを実行してみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672909/3947ee2e-113b-7098-61bd-3545e23cbbb0.png)
しっかりと取得できていて、空白なども消せていました。

次にこれをdiscordのbotプログラムに組み込んでいきます。
`discord.py`に入っているループ機能を使用して60秒に一回ループ、かつその中で指定した時刻になったらスクレイピングしテキストチャンネルに投稿というプログラムを組みました。ソースは以下のよう。

```python:bot.py
#coding:UTF-8
import discord
from discord.ext import tasks
from datetime import datetime
import requests
from bs4 import BeautifulSoup

TOKEN = "hoge" #トークン
CHANNEL_ID = hoge #チャンネルID 

# 接続に必要なオブジェクトを生成
client = discord.Client()

# 60秒に一回ループ
@tasks.loop(seconds=60)
async def loop():    
    now = datetime.now().strftime('%H:%M')
    
    if now == '20:05':
        await client.wait_until_ready()
        channel = client.get_channel(CHANNEL_ID)
        #先ほどのコード(soup.py)
        res= requests.get("https://stopcovid19.metro.tokyo.lg.jp/cards/number-of-confirmed-cases/")
        soup = BeautifulSoup(res.text,"html.parser")
        con = soup.find("span",class_="DataView-DataInfo-summary")
        con=soup.find("small",class_="DataView-DataInfo-summary-unit").extract()
        text=con.get_text()
        text= text.strip() 

        await channel.send("本日の東京都の感染者数は"+text+"人です") 

 #おまけ部分。指定したワードに反応してコメントを返す機能       
@client.event
async def on_message(message):
    # メッセージ送信者がBotだった場合は無視
    if message.author.bot:
        return
    # 「新宿」と発言したら「密」が返ってくる
    if message.content == '新宿':
        await message.channel.send('密です')
    
    if message.content == '江戸川区':
        await message.channel.send('密です')

#ループ処理実行
loop.start()
# Bot起動
client.run(TOKEN)
```
時刻を20:05にしてるのは、今回使用しているサイトの感染者数の更新が20時であると推測したため。
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672909/bcbe8cdc-68bc-b50e-5cc1-9a0886cd2f89.png)

※サイトには20時と書いてありましたが、日によってまちまちで今回は10分ほどであったためこのような結果になりました。設定時刻は余裕をもって20時30分とかがよさそう。
動作の確認ができたので、これをVPSのDocker上で起動します。

### Dockerに輸送
`docker run -it --name hoge python /bin/bash`
でpython環境を構築
`docker exec -it hoge bash`
で中にもぐって作業開始。
まずは、pipで必要なライブラリをインストール。
`pip install beautifulsoup4`
`pip install requests`
`pip install discord`
vimがなくて編集できないので
`apt-get update`
`apt-get install vim`
で入れます。
デフォルトのタイムゾーンがUTCなので
`ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime`
このコマンドを使用してタイムゾーンを変更。
その後適当にフォルダ作ってその中に`bot.py`をつくり先ほどのソースをコピペします。これで完成。
`python bot.py`で起動すればdiscord上にbotが立ち上がり、指定時刻に都内の本日のコロナ感染者数を報告してくれます！

## 改善点
今回はサイトが20時に更新されると推測し決め打ちを行っていますが、理想形はサイトが更新されると同時にスクレイピングを行いbotが報告してくれるという形です。
ループの中で、そのループで取得してきたやつと前回取得したデータを比較し一致したらそのまま違ったら更新しbotが報告する、というプログラムを書けばできそうだなーと思ったので改善したらまた別の記事にまとめて投稿します。