動機
僕の通う東京工業大学っていうFラン大学では、
講義資料や課題がOCWiっていう大学の管理してるウェブサイトに上がるんですけど
ログインするのに、毎回パスワードとマトリクスコードが必要です。
それだけでなく、クソみたいに古臭いWebサイトだし
めちゃめちゃ重いし、講義資料をダウンロードするだけでも一苦労なんですよ(汗...
ということで自動化してしまおうという発想に至りました。
まぁ、僕は設計しただけで、実装は友達がしたんですけど、同じような境遇にある大学生の役に立てればいいかなぁという事で。
ぜひ皆さんの大学でも実装してみてください。
Iris(Interval request information service)
解決すべき課題(Issue to be Solved)
現状
- 更新された授業資料をダウンロードするのは手間
- 講義資料をダウンロードした人がSlackの各授業チャンネルにアップロードしている
課題
- めんどくせぇ
UserStory
- IrisがOCWiから講義資料をとってくる
- GoogleDriveAPIを使ってポストする
- 講義資料がアップデートされていたらSlackに通知をとばす
- SlackとGoogleDriveは連動しているので、Slackから資料が見れる
- GoogleDriveから必要な資料を見れる
設計
使用するライブラリ
- selenium(認証突破)
- urllib(ファイルダウンロード)
- beautifulsoup(スクレイピング)
- google-api-python client
- PyDrive(google-api-python clientのラッパー)
実行の自動化
一般的にはcrontabがLinuxだと使われるみたいですが、
自動化するときは、自分でデーモンを作ってあげた方が使いやすいんですよ。
通知とか分散実行とかしたいときはdigdagとかAirflowとか使いますけどね。
そういった難しい事する記事はまた書くとして、
今回はシンプルにLinuxのデーモンを新しく作ってしまいます。
重要になるファイル
-
/etc/systemd/system/[デーモン名].service
これがユニットファイルと呼ばれているもので、ここのディレクトリ下にファイルを置くだけで、systemctlにサービスだと認識してもらえます。
以下のような感じで作りました。
[Unit]
Description = This is OCWi login service daemon
After=local-fs.target
ConditionPathExists=自動起動したいプログラムへのパス(/opt/[パッケージ名]/bin)
[Service]
ExecStart=定期実行するコマンド(これがそのまま実行されます)
Type=oneshot
[Install]
WantedBy=multi-user.target
After=local-fs.target
はストレージのマウントが終わってから実行してくださいって意味です。
この辺はちょっとLinuxの(大学生にとっては)難しい話になるのですが、
/usr, /opt, /var
の三つのディレクトリってLinuxにはあるじゃないですか。
あの3つは、ローカルファイルシステムのパーティションが別に区切られていることがあるんです。というか区切っていいとされているんです。
パーティションが分かれているということはすなわち、使用する際にマウントして使用するということである。
メリットは単純で、更新作業などがシステムへの影響を及ぼしにくかったり、
swap領域のパーティションを新たに設けたりできるということ。
/optはパッケージがもっさり入っていて、容量もでかいし、更新も頻繁だからパーティション分けてもいいよってされてるんですよ。
だから/opt以下を使用するときは、使っているときは気がつかないけれど、実はメモリの論理領域にアクセスしてマウントポイントを得てからマウントして使用しているってことです。
だから、今回はマウント前に実行してしまうとそんなディレクトリないよ〜ってエラーで返ってきちゃうので、念の為、マウントしてから実行してねって言ってます。
/opt/[パッケージ名]/bin/[デーモン名].sh
systemdのユニットファイル中のExecStartのところにPyhton3 [ファイル名]
としてもいいのですが、
拡張性が担保されないことと, systemdとプログラムの切り分けができないことから
shell script側で実行してあげようと思います。
exec python3 /opt/iris/bin/iris.py
binと.shと.pyにchmod755
とchown root:root
しておくのは忘れないでください。
permission deniedで落ちるので。
$ sudo systemctl daemon-reload
$ sudo systemctl enable iris.service
$ sudo systemctl start iris.service
[Service]
ExecStart=/opt/iris/bin/iris.sh
Type=oneshot
[Install]
WantedBy=multi-user.target
jrny@iris:/etc/systemd/system$ cat iris.timer
[Unit]
Description=timer of iris
[Timer]
OnCalendar=*-*-* 5:00
Persistent=true
[Install]
WantedBy=timers.target
タイマーは拡張子が .timer の systemd のユニットファイルです。
[Timer]以下のOnCalendarの部分で頻度を決めています。
OnBootSec=15min
なら15分おきに実行されます。
OnCalendar=Mon,Tue *-*-01..04 12:00:00
毎月の1日から4日まで、月曜日か火曜日の場合にのみ、午後0時
Sat *-*-1..7 18:00:00
毎月第1土曜日
OnCalendar=*-*-* 4:00:00
毎朝四時
という感じで指定できます。
基本はcrontabと同じですね。
sudo systemctl daemon-reload
で反映
sudo systemctl enable iris.timer
で有効化
``
よくある失敗
permisson error
ls -l filename
の一番左にシステムコールに関する権限が出るので確認してください。
知ってると思いますが、所有者、グループ、その他の順で出ます。
実行権限がなかったら、755とかにしておくと大丈夫です。
systemctl側の問題
systemctl status iris.service
で確認できるので、確認してください。
プログラム側の問題
journalctl -u ユニットファイル名
でログが見れるので確認してください。
Seleniumを使ったログイン
driver.find_element_by_name('usr_name').send_keys(secret.studentu_num)
driver.find_element_by_name('usr_password').send_keys(secret.password)
driver.find_element_by_name('OK').click()
Seleniumではelement_by_name
で要素を取得して
値を代入することができるので、上記のような処理でSessionIDを取得できます。
あとは講義資料のあるページをスクレイピングして、資料をダウンロードするだけです。
https://qiita.com/memakura/items/20a02161fa7e18d8a693
この記事が非常にわかりやすくまとまっているのでご参考ください。
GoogleDriveとIrisの接続
GCSって選択肢もあったんですけど、極力GCPに課金したくなかったので、Driveにしました。
認証はGoogleアカウントの鍵で繋げます。
ファイアーウォールとかポート開けたりしなくていいので簡単です。
gauth = GoogleAuth()
gauth.LocalWebserverAuth()
drive = GoogleDrive(gauth)
ってやるときにログインしろって言われるので言われるがままにしておけば、直下にcredential.jsonできてます。
ファイルのアップロード自体はupfilesという関数があるので、それ使うとファイルがアップされます。