MyDocomoから今月の通信量を取得してLINEで通知する

# やったこと
1日1回、MyDocomoから今月の通信量を取得してLINE Notifyでお知らせするプログラムを作りました。
また、これをHerokuで定期実行する環境を作りました。

## 使ったもの

### 1日1回実行するために...
* Heroku
* Heroku Scheduler

### 今月の通信量を取得するために...
* Python3 (言語)
* Selenium (ブラウザ自動操作ツール)
* Chrome (ブラウザ)
* Chrome Webdriver (Chromeを自動操作するためのドライバ)

# 作成したファイル
今回作成したファイルは以下のとおりです。

``` sh
$ ls
Procfile
main.py
requirements.txt
README.md
mydocomo.py
```

## Procfile
> Procfileは、Herokuのプラットフォーム上にあるアプリケーションのdynosにより実行されるコマンドが 何であるかを宣言するためのメカニズム
(https://github.com/herokaijp/devcenter/wiki/procfile より引用)

``` :Procfile
web: python main.py
```

## `main.py`
Webアプリに適用するダミーのコード。
よく分かってはいませんがWebアプリにはダミーを置いておくもののようです。
おそらく、誰かにWebアプリに対してリクエストを送られたタイミングで定期実行用のプログラムが動かないようにするため？だと解釈しています。

``` Python:main.py
# -*- coding: utf-8 -*-

import os
from bottle import route, run


@route("/")
def hello_world():
    return "" # ここで返す内容は何でもよい

run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
```

## `requirements.txt`
必要なライブラリを記載しておくファイル。
これに記載したライブラリは、デプロイのたびにHerokuの環境へ自動インストールされるらしい。便利。
pipをインストール済みの場合、↓のコマンドを打つとインストール済みのライブラリを出してくれて楽でした。

``` sh
$ pip freeze -l > requirements.txt
```

``` :requirements.txt
astroid==2.2.5
beautifulsoup4==4.7.1
bottle==0.12.16
certifi==2019.3.9
chardet==3.0.4
chromedriver-binary==74.0.3729.6.0
Click==7.0
colorama==0.4.1
Flask==1.0.2
future==0.17.1
idna==2.8
isort==4.3.16
itsdangerous==1.1.0
Jinja2==2.10
lazy-object-proxy==1.3.1
line-bot-sdk==1.8.0
MarkupSafe==1.1.1
mccabe==0.6.1
PyLink==0.3.3
pylint==2.3.1
pyserial==3.4
requests==2.21.0
rope==0.14.0
selenium==3.141.0
six==1.12.0
soupsieve==1.9
typed-ast==1.3.1
urllib3==1.24.1
Werkzeug==0.15.2
wrapt==1.11.1
```
インストール済みのライブラリが全て出てくるので、今回の実行には不要なライブラリも含まれちゃうみたいです。

## `README.md`
なくても良いので割愛します。

## `mydocomo.py`
定期実行するプログラム本体。
MyDocomoにログインする際のID、PW、LINE Notifyのトークンは、Herokuの環境変数に設定しました。

``` Python:mydocomo.py
# coding: UTF-8
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import chromedriver_binary
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import os
import requests
import sys

# 環境変数からトークンなどの情報を取得
notify_access_token = os.getenv('NOTIFY_ACCESS_TOKEN', None)
docomo_id = os.getenv('DOCOMO_ID', None)
docomo_pw = os.getenv('DOCOMO_PW', None)
if notify_access_token is None:
    print('Specify NOTIFY_ACCESS_TOKEN as environment variable.')
    sys.exit(1)
if docomo_id is None:
    print('Specify DOCOMO_ID as environment variable.')
    sys.exit(1)
if docomo_pw is None:
    print('Specify DOCOMO_PW as environment variable.')
    sys.exit(1)

# 通信量の取得
def getLog():

    # Selenium用オプション
    op = Options()
    op.add_argument("--disable-gpu")
    op.add_argument("--disable-extensions")
    op.add_argument("--proxy-server='direct://'")
    op.add_argument("--proxy-bypass-list=*")
    op.add_argument("--headless")

    driver = webdriver.Chrome(chrome_options=op)
    driver.get('https://www.nttdocomo.co.jp/auth/cgi/mltdomanidlogin?rl=https%3A%2F%2Fwww.nttdocomo.co.jp%2Fmydocomo%2F')

    # ID入力
    id = driver.find_element_by_id("Di_Uid")
    id.send_keys(docomo_id)

    # 「次へ」クリック
    login_button = driver.find_element_by_name("subForm")
    login_button.click()

    # PW入力
    password = driver.find_element_by_id("Di_Pass")
    password.send_keys(docomo_pw)

    # 「ログイン」クリック
    login_button = driver.find_element_by_name("subForm")
    login_button.click()

    # 通信量のページへ遷移
    driver.get("https://www.nttdocomo.co.jp/mydocomo/data/")
    dataSpan = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "#mydcm_data_data-08 > div > dl > dd > p.mydcm_data_data-09-1 > span.card-t-ssnumber.t-bold.mydcm_data_data-10-1"))
    )
    return dataSpan.text

def main():
    mount = getLog()

    url = "https://notify-api.line.me/api/notify"
    token = notify_access_token
    headers = {"Authorization" : "Bearer "+ token}

    message =  '今月の通信量は ' + mount + ' GBです。'
    payload = {"message" :  message}

    r = requests.post(url ,headers = headers ,params=payload)

if __name__ == '__main__':
    main()
```

# Herokuの設定

GUIが大好きなのでHerokuのアプリケーションのページから設定しました。
Heroku CLIをインストールしてCUIで設定しても良いです。

## buildpackの追加
Heroku上でChromeとChrome Webdriverを使うためにbuildpackというものを追加します。
`Settings`タブの`buildpacks`から`Add buildpack`をクリックして、以下のURLを1つずつ追加します。

```
https://github.com/heroku/heroku-buildpack-google-chrome.git
https://github.com/heroku/heroku-buildpack-chromedriver.git
```

## 環境変数の設定

`Settings`タブの`Config Vars`から`Reveal Config Vars`をクリックして、以下の変数を追加します。

| 変数名 | 値 |
| --- | --- |
|CHROME_BINARY_LOCATION|/app/.apt/usr/bin/google-chrome|
|CHROME_DRIVER_PATH|chromedriver|
|DOCOMO_ID|`hogehoge＠hogehoge.com`(MyDocomoのID)|
|DOCOMO_PW|hoge(MyDocomoのパスワード)|
|NOTIFY_ACCESS_TOKEN|取得したLINE Notifyのトークン|
|TZ|Asia/Tokyo|

TZは今回設定しなくても良いですが、合わせておいた方が何かと便利かと思います。

### ※とりあえず動作テスト

ここまで設定しておいてデプロイすれば、（おそらく）一旦は動くようになっていると思います。
コンソールから`mydocomo.py`を実行してみます。
Herokuのアプリケーションのページの右上にある`More`から、`Run console`をクリックし以下のコマンドを実行します。

```
python mydocomo.py
```
Herokuのサーバーがスリープ状態だと実行までに時間がかかるみたいです。
しばらく待ってもLINEの通知が来なかったらLog等を確認してみてください。
問題なければ30秒も待てば通知が来ると思います。

## Heroku Schedulerの設定
※Heroku Schedulerのインストール方法は割愛します。

`Resource`タブの`Heroku Scheduler`をクリックし、ジョブの管理画面を開きます。
ジョブの管理画面で`Add Job`をクリックします。

### Schedule
`Schedule`は`Every day at...`を選び、
時間は私の場合、`3:00 PM`を選びました。

設定する時間は`世界協定時`での設定になるため、
日本の時刻は`設定する時間+9時間`で考えます。
→UTC午後3時を設定すると、日本時刻では午前0時となります。

### Run Command
`Run Command`は先程の動作テストで流したコマンドです。

```
python mydocomo.py
```

これで毎日0時に今月の通信量がLINE Notifyに通知されるようになります。

# 課題

Pythonの勉強がてら試しに作ったので取得するデータは`今月の通信量`だけですし、通知されるメッセージも簡素です。
多少の実用性を考えるのであれば、使いすぎた日の翌日だけ通知するとかにした方が良さそうです。

# 参考ページ
大変お世話になりました。
* [twitter定期botをHerokuにデプロイ](https://qiita.com/nkg/items/0379a0a9c45af58640f5)
* [簡単！Herokuで動くTwitter botをPythonで実装する](https://qiita.com/enomotok_/items/41275dd904c8aa774e72)
* [HerokuにChromeとChrome driverを入れておく](https://ikapblg.blog.fc2.com/blog-entry-147.html)