pythonの業務自動化で東京オリンピックメタル情報を毎日受信してみましょう


## 当記事で実現していること
- webから毎日のオリンピックメタルランキング(TOP10)の情報を取得する
- 取得した情報はEXCELに保存する
- オリンピック開始日から当日分のメタルランキング(TOP10)をEXCELから取得する
- 当日のメタルランキング(TOP10)をグラフで表示する
- メタルランキング(TOP10)の毎日のメタル推移をグラフで表示する
- 当日のメタルランキング(TOP10)の情報、上記作ったグラフを毎日一定時間に自動でメールでお知らせする

## 当記事で共有する機能
- pythonでWEBからデータ取得
- pythonでEXCELにデータ書き込みとEXCELからデータ読み取り
- pythonで表データをグラフで表示する
- pythonでメール送信する「添付ファイルなし」
- pythonでメール送信する「添付ファイルあり」
- windowsのスケジューラの使い方

## 早速作りましょう

###  必要なライブラリ
``` tokyo2020.py
# 使てない物もあるかもしれません
import sys
from datetime import date
import pandas as pd
import openpyxl
import matplotlib.pyplot as plt
import matplotlib.style
import smtplib, base64
from email.message import EmailMessage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email.header import Header
from email.utils import formatdate
from email import encoders
import os.path
```

###  オリンピックメタルランキング(TOP10)の情報を取得する関数
``` tokyo2020.py
def read_olympic_info_from_web():
    # 公式サイトから情報を取得
    url = 'https://olympics.com/tokyo-2020/olympic-games/ja/results/all-sports/medal-standings.htm'
    df = pd.read_html(url)
    #コンテンツの中からTOP10位の情報を取得
    olympic_info = df[0].head(10)
    # カラム名を整理
    olympic_info = olympic_info.rename(columns={'チーム／NOC': 'チーム', 'Unnamed: 2': '金', 'Unnamed: 3': '銀','Unnamed: 4': '銅',})
    return olympic_info
```
> 公式サイトは：[東京2020オリンピック公式ウェブサイト](https://olympics.com/tokyo-2020/olympic-games/ja/results/all-sports/medal-standings.htm)
>サイト(リソース)の中から一番目をtable要素から前頭の10行を選択
>カラム名を自分で分かりやすいように整理する
>処理後データを返す


###  取得したデータをEXCELに保存する

#### まずEXCELのサンプルをpyファイルがあるディレクトリに作成する、以下のように

tokyo2020
 |----tokyo2020.xlsx
 |----tokyo2020.py

次にtokyo2020.xlsxの中にサンブルシートを作成する、以下のように

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/2e6840d1-83c9-857a-1135-ad65df467c4f.png)

>表のタイトルとwebデータの整理したカラム名を同様こと

#### EXCELにデータを書き込み
``` tokyo2020.py
def write_olympic_info_to_excel():
    olympic_info = read_olympic_info_from_web()
    # mm-dd形式で毎日のシートを作成、情報を保存する
    today = date.today()
    my_date = str(today.month) + '-' + str(today.day)
    wb = openpyxl.load_workbook('tokyo2020.xlsx')
    sheet = wb.copy_worksheet(wb['サンブル'])
    sheet.title = my_date
    # データ書き込み
    for i,row in olympic_info.iterrows():
        index = i + 2
        sheet.cell(row = index, column = 1).value = row['順位']
        sheet.cell(row = index, column = 2).value = row['チーム']
        sheet.cell(row = index, column = 3).value = row['金']
        sheet.cell(row = index, column = 4).value = row['銀']
        sheet.cell(row = index, column = 5).value = row['銅']
        sheet.cell(row = index, column = 6).value = row['合計']
        sheet.cell(row = index, column = 7).value = row['合計別順位']
        sheet.cell(row = index, column = 8).value = row['NOCコード']
        sheet.cell(row = index, column = 9).value = my_date
    wb.save('tokyo2020.xlsx')
```
> 「サンプル」シートをコピーして別シート作成している
>新規作成したシート名を当日の日付(mm-dd)で命名している
>WEBから取得したデータをループして行ごとにEXCELシートに書き込んでいる
>既存の`'tokyo2020.xlsx`を上書き

###  データをEXCELから読み取り
``` tokyo2020.py
def read_olympic_info_from_excel():
    input_file = pd.ExcelFile('tokyo2020.xlsx')
    sheet_names = input_file.sheet_names
    # workbookの全てのシートから情報を読み込み
    dic = pd.read_excel('tokyo2020.xlsx', sheet_name = sheet_names)
    olympic_info = pd.concat(dic, axis=0, ignore_index=True)
    return pd.DataFrame(olympic_info, columns = ['順位','チーム','金','銀','銅','日程'])
```
>すべてのシートのデータを取得してPandasのDataFrameに変換している
>変換後のDataFrameから必要な情報を取得する
>取得したデータを返す

###  TOP10のチーム情報をグラフで表示
``` tokyo2020.py
def olympic_info_show_barh():
    # 最新オリンピック情報を取得
    olympic_info = read_olympic_info_from_web()
    olympic_info = pd.DataFrame(olympic_info, columns = ['順位','チーム', '金']).sort_values(by='金',ascending=True)
    # 最新情報をまとめて描画
    fig, ax =plt.subplots()
    ax.barh(olympic_info['チーム'], olympic_info['金'], color='teal', label = '金メタル数')
    ax.legend()
    # 画像を表示
    #plt.show()
    # 画像を保存
    plt.savefig('tokyo2020.png') 
```
>ここは直接WEBから取得したデータを使っている
>グルーピング処理で`チーム`の`毎日`の取得した`金`,`銀`,`銅`の数字を分割している
>横棒グラフでランキング金メタルの数の大き準で表示する

![tokyo2020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/a1cf14cf-96b5-6d34-cadd-09dbf1481e20.png)


###  TOP10のチーム情報をグラフで表示「チーム別」
``` tokyo2020.py
def olympic_info_show_plot():
    # 最新オリンピック情報を取得
    olympic_info_from_web = read_olympic_info_from_web()
    # excelに保存したデータを読み込み
    olympic_info = read_olympic_info_from_excel()
    # チーム、日程でグルーピング
    olympic_info = olympic_info[['順位','金', '銀','銅']].groupby([olympic_info['チーム'], olympic_info['日程']])
    olympic_info = olympic_info.sum().reset_index()
    # 折れ線グラフのスタイルを設定
    matplotlib.style.use('ggplot')
    for i in range(0,10):
        fig, ax = plt.subplots()
        # チーム別にデータを取得
        data = olympic_info[olympic_info['チーム'] == olympic_info_from_web.iloc[i,1]]
        # 折れ線グラフ描画
        ax.plot(data['日程'], data['金'], color='#FFCC00',marker='o', label='金')
        ax.plot(data['日程'], data['銀'], color='#33CCFF', marker='o', label='銀')
        ax.plot(data['日程'], data['銅'], color='#990000', marker='o', label='銅')
        ax.set_title(olympic_info_from_web.iloc[i,1])
        ax.legend()
        #plt.show()
        plt.savefig(olympic_info_from_web.iloc[i,1] +'.png') 
```
>ここはEXCELから取得したデータを使っている
>グラフのタイトルにWEBから取得したデータをチーム名を表示している
>横軸は日程、縦軸はメタル数
>折れ線グラフで金メタル、銀メタル、どうメタルの毎日獲得した数を描画

![アメリカ合衆国.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/ef7783c2-ab01-5766-7db6-3fc3f2cddaa5.png)

![中華人民共和国.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/4a49bb7b-f051-a5d9-4b82-815370325da8.png)

![日本.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/64fa3745-5d5c-d530-0a4f-f2b39fcd3254.png)

![英国.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/339e53c5-6c96-c26b-5d12-ca151caa60e6.png)

![ROC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/c769ece3-b507-35e4-6c18-0f7a08713255.png)

![オーストラリア.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/d61378ef-02f8-94e6-b2ba-3233d5b80d51.png)

![イタリア.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/93052214-7947-aeec-0df8-ed14e68d5d99.png)

![ドイツ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/a527d32a-b65d-a642-e445-edbce7880c7e.png)

![フランス.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/21e8dc35-820f-c673-c390-457342dbda64.png)


![オランダ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/3ed92e48-7e92-a1a0-a387-23cc2fd31d87.png)

###  メール送信「添付ファイルあり」
``` tokyo2020.py
def send_olympic_info_temp(title, html) :
    # 各種情報
    mail_to   = "送信先"
    mail_from = '送信元'
    password = 'パスワード'

    # STMPサーバ設定
    smtp_server = 'smtp.gmail.com'
    port = 587

    # サーバに接続
    server = smtplib.SMTP(smtp_server, port)
    server.starttls()
    server.login(mail_from, password)

    #送信準備
    #msg = MIMEMultipart('alternative')
    msg = MIMEMultipart()
    #msg = MIMEMultipart()
    msg['Subject'] = title
    msg['From'] = mail_from
    msg['To'] = mail_to
       
    related = MIMEMultipart('related')
    alt = MIMEMultipart('alternative')
    related.attach(alt)
    
    part = MIMEText(html, 'html')
    msg.attach(part)
    
    # 最新オリンピック情報を取得
    olympic_info_from_web = read_olympic_info_from_web()
    
    try:
        # 添付ファイルの追加「TOP10チーム金メタルランキング」
        with open('tokyo2020.png', mode='rb') as png:
            png_data = png.read()
            attach_file = MIMEApplication(png_data)
            attach_file.add_header('Content-Disposition', 'attachment', filename='tokyo2020.png')
            msg.attach(attach_file)
        
        # 添付ファイルの追加「TOP10チーム別日推移メタル数」
        for i in range(0,10):
            team_name = olympic_info_from_web.iloc[i,1]
            with open(f'{team_name}.png', mode='rb') as png:
                png_data = png.read()
            attach_file = MIMEApplication(png_data)
            attach_file.add_header('Content-Disposition', 'attachment', filename=team_name + '.png')
            msg.attach(attach_file)
                
        # メール送信
        try:
            server.send_message(msg)
        except:
            print('本日のオリンピック情報送信完了できませんでした。')
        else:
            print('本日のオリンピック情報送信完了しました。')
            
    except FileNotFoundError:
        print('ファイルがありませか送信しませんでした。')
    finally:
        # SMTP セッションの終了と TCP コネクションの切断
        server.quit()
```
>ここ送信元メールはGmailを使っている
>グラフのタイトルにWEBから取得したデータをチーム名を表示している
>送信内容は上記で取得したWEBデータと作ったグラフの画像
>メール送信時は`send_olympic_info_temp(title, html) 'の引数にデータを渡す（例：title:本日のオリンピックメタル情報、html：WEBから取得したデータをHTML形式で渡す）

## コードを定期（毎日）実行する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148710/334f5001-83f1-da2f-ac12-1702760fd9b2.png)

- Windowsの`スケジューラ`ツールを使って上記の`tokyo2020.py`を毎日の一定時間に自動で実行するようにスケジューリングする
- Windowsの`スケジューラ`での設定方法はネットでたくさんあるのでそちらを参照してください、本記事で割愛

## 最後に
- もっといいやり方があるかもしれません
- 記事の中に間違いがあるかもしれません、見つかったら指摘お願い。。