Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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

準備

準備として、大学のポータルサイトへの自動ログインを以下のコードで書きました。

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.getwebdriver.find_element_by_〇〇の中身をサイトの形式に合わせて変えると動くと思います。

スクレイピング

週末は時間割がなく、空欄が返ってくるのでif文で簡単な関数を作っておきました。

#週末は空欄になってしまうので...
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の中身を確認しておくと、

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

明日の時間割
""""""""""""
時限
情報科学
"""""""""""" 

いい感じにスクレイピング出来ました。

LINE Notifyで通知

(https://notify-bot.line.me/ja/ )にアクセスしログイン後、マイページにてトークンを発行します。この際、モバイルからだとトークン発行画面が表示されなかったのでpcからアクセスすると表示されるはずです。以下のコードは「Pythonでスクレイピングした情報をLINE Notifyを使って通知する」を参考にさせて頂きました。

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)

最終的なコード

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

おまけ

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で実行!的なことが出来ます。)callactivate.batの場所を書いています。activate.batの場所が違う場合があるので確認して記述します。

タスクスケジューラ

windowsのスタートメニューの検索欄からタスクスケジューラを検索し、起動します。
タスクの作成を選択し、操作のタブから新しい操作を選択し、参照から、作成したバッチファイルを選択します。後は名前とトリガーを記述するだけで完了です。
自分はpcをスリープからログインした際に実行されるように、トリガーのタスクの開始を「ワークステーションアンロック時」に設定しました。
詳しくはこちらを参考にしてください。

おわりに

pythonのコードを書くところまでは順調だったのですが、意外にも、タスクスケジューラの使い方で結構詰まりました。
スクレイピングしたり何かしらのAPI使うことは楽しいので、これからも気が向けばやってみようと思いました。

参考にした記事

Pythonでスクレイピングした情報をLINE Notifyを使って通知する
Python3 文字列中に変数展開したい
XPathのまとめ、要素の参照方法いろいろ
初心者でもわかるWindowsタスクスケジューラの使い方
タスクスケジューラ:トリガー

kitarikes
python// 機械学習など
https://twitter.com/ki_rii9
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away