時間割をLINEで自動通知する。(selenium+LINE Notify)

#はじめに
今回の目標は最新の時間割を大学のポータルサイトからスクレイピングしてLINEnotifyで通知を受け取れるようにすることです。
動機は先日、大学の授業変更に気付けず1コマ分の出席を無駄にしてしまった(せっかく早起きしたのに...)からです。
#準備
準備として、大学のポータルサイトへの自動ログインを以下のコードで書きました。

```python:
from selenium import webdriver 
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--headless')
webdriver = webdriver.Chrome(options=options)

webdriver.get("＜ポータルサイトのURL＞")
#Xpathで指定
webdriver.find_element_by_xpath('//*[@id="userId"]').send_keys("USERID")
webdriver.find_element_by_xpath('//*[@id="password"]').send_keys("PASSWORD")
#クリックするボタン
webdriver.find_element_by_css_selector("#loginButton").click()
```
optionでheadlessを付けることでブラウザが立ち上がらなくなるので便利です。
他のサイトの場合は`webdriver.get`と`webdriver.find_element_by_〇〇`の中身をサイトの形式に合わせて変えると動くと思います。
##スクレイピング
週末は時間割がなく、空欄が返ってくるのでif文で簡単な関数を作っておきました。

```python:
#週末は空欄になってしまうので...
def check (subjects):
    x=[]
    for subject in subjects:
       x.append(subject.text)
    
    if x == ['']:
        return '''""""""""""""
授業はありません♪
"""""""""""" '''
    else:
        for subject in subjects:
            x=subject.text
        return '''""""""""""""
%s
"""""""""""" ''' % x

                                       
try:
       
    today_list = webdriver.find_elements_by_xpath('//*[@id="weekly"]/tbody/tr[3]/td[2]')
    tomorrow_list = webdriver.find_elements_by_xpath('//*[@id="weekly"]/tbody/tr[3]/td[3]')
    
    msg='''
今日の時間割
%s

明日の時間割
%s'''%(check(today_list),check(tomorrow_list))
    print(msg)
          
    
finally:
    webdriver.quit()
```
ここでmsgの中身を確認しておくと、

```python

print(msg)
>>今日の時間割
""""""""""""
１時限
基幹物理学ⅠＢ
２時限
中国語Ⅱ
３時限
課題協学科目
４時限
課題協学科目
"""""""""""" 

明日の時間割
""""""""""""
３時限
情報科学
"""""""""""" 
```
いい感じにスクレイピング出来ました。
#LINE Notifyで通知
(https://notify-bot.line.me/ja/ )にアクセスしログイン後、マイページにてトークンを発行します。この際、モバイルからだとトークン発行画面が表示されなかったのでpcからアクセスすると表示されるはずです。以下のコードは[「Pythonでスクレイピングした情報をLINE Notifyを使って通知する」](https://qiita.com/tuna27/items/8a28db55d7c1165ee0dc)を参考にさせて頂きました。

```python
import urllib.request
import sys
LINE_TOKEN =  "＜取得したTOKEN＞"
LINE_NOTIFY_URL = "https://notify-api.line.me/api/notify"

def class_info(msg):
    method = "POST"
    headers = {"Authorization": "Bearer %s" % LINE_TOKEN}
    payload = {"message": msg}
    try:
        payload = urllib.parse.urlencode(payload).encode("utf-8")
        req = urllib.request.Request(
            url=LINE_NOTIFY_URL, data=payload, method=method, headers=headers)
        urllib.request.urlopen(req)
    except Exception as e:
        print ("Exception Error: ", e)
        sys.exit(1)

def main():
    class_info(msg)
```
#最終的なコード
```python:class_notify.py
from selenium import webdriver 
from selenium.webdriver.chrome.options import Options
import urllib.request
import sys

options = Options()
options.add_argument('--headless')
webdriver = webdriver.Chrome(options=options)

webdriver.get("<URL>")
#Xpathで指定
webdriver.find_element_by_xpath('//*[@id="userId"]').send_keys("USERID")
webdriver.find_element_by_xpath('//*[@id="password"]').send_keys("PASSWORD")
#クリックするボタン
webdriver.find_element_by_css_selector("#loginButton").click() 
                                       
#週末は空欄になってしまうので...
def check (subjects):
    x=[]
    for subject in subjects:
       x.append(subject.text)
    
    if x == ['']:
        return '''""""""""""""
授業はありません♪
"""""""""""" '''
    else:
        for subject in subjects:
            x=subject.text
        return '''""""""""""""
%s
"""""""""""" ''' % x

                                       
try:
       
    today_list = webdriver.find_elements_by_xpath('//*[@id="weekly"]/tbody/tr[3]/td[2]')
    tomorrow_list = webdriver.find_elements_by_xpath('//*[@id="weekly"]/tbody/tr[3]/td[3]')
    
    msg='''
今日の時間割
%s

明日の時間割
%s'''%(check(today_list),check(tomorrow_list))
    print(msg)
          
    
finally:
    webdriver.quit()

LINE_TOKEN =  "<TOKEN>"
LINE_NOTIFY_URL = "https://notify-api.line.me/api/notify"

def class_info(msg):
    method = "POST"
    headers = {"Authorization": "Bearer %s" % LINE_TOKEN}
    payload = {"message": msg}
    try:
        payload = urllib.parse.urlencode(payload).encode("utf-8")
        req = urllib.request.Request(
            url=LINE_NOTIFY_URL, data=payload, method=method, headers=headers)
        urllib.request.urlopen(req)
    except Exception as e:
        print ("Exception Error: ", e)
        sys.exit(1)

def main():
    class_info(msg)


if __name__ == '__main__':
    main()
```
そして以下のように問題なく通知がきました。
![1572336804873.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458430/a150e434-3eb1-d98b-6122-811e654ada4d.jpeg)
#おまけ
pythonファイルを手動で実行するのはめんどくさいので、今回は簡単なバッチファイルを作り、windowsのタスクスケジューラで自動実行させようと思います。
##バッチファイル作成
```:hoge.bat
cd \
cd C:\Users\ユーザー名\〇〇〇 (←実行するpythonファイルの階層まで移動)
call C:\\Users\\ユーザー名\\Anaconda3\\Scripts\\activate.bat
python class_notify.py
```
（```cd \```をせずに階層まで移動しようとするとエラーが出たため、いったん```cd \```を付けています。）
anacondaの環境で実行したいので(anaconda promptで実行！的なことが出来ます。)```call```で```activate.bat```の場所を書いています。```activate.bat```の場所が違う場合があるので確認して記述します。
##タスクスケジューラ
windowsのスタートメニューの検索欄からタスクスケジューラを検索し、起動します。
```タスクの作成```を選択し、操作のタブから新しい操作を選択し、参照から、作成したバッチファイルを選択します。後は名前とトリガーを記述するだけで完了です。
自分はpcをスリープからログインした際に実行されるように、トリガーのタスクの開始を「ワークステーションアンロック時」に設定しました。
詳しくは[こちら](http://minto.tech/windows-taskscheduler/2/)を参考にしてください。
#おわりに
pythonのコードを書くところまでは順調だったのですが、意外にも、タスクスケジューラの使い方で結構詰まりました。
スクレイピングしたり何かしらのAPI使うことは楽しいので、これからも気が向けばやってみようと思いました。
#参考にした記事
[Pythonでスクレイピングした情報をLINE Notifyを使って通知する](https://qiita.com/tuna27/items/8a28db55d7c1165ee0dc)
[Python3 文字列中に変数展開したい](https://chaika.hatenablog.com/entry/2018/08/16/090000)
[XPathのまとめ、要素の参照方法いろいろ](https://webbibouroku.com/Blog/Article/xpath)
[初心者でもわかるWindowsタスクスケジューラの使い方](http://minto.tech/windows-taskscheduler/2/)
[タスクスケジューラ：トリガー](https://forsenergy.com/ja-jp/taskscheduler/html/04ea6e78-1d5c-464d-8ba2-b6a441adbdd7.htm)