課題提出スクリプトで楽ちんしたい!
本記事は ICON Advent Calendar 2018 1日目の記事です。
はじめに
「課題終わった!でも課題の提出締切まであと10秒しかない(絶望)」
皆さんはこのような経験はありますか?僕は何度もありました。 課題をギリギリ終えられたものの、提出ページへのログインや提出物の選択といった提出手順に時間がかかってしまい遅提出扱いに。実に悔しい。 早めに課題に取りかかれば良いのでは?という話なんだけど、習慣というものはそう簡単に変えられるものではないわけで...。
そこで、そんな怠惰な僕が現状を打破するため作成した課題提出スクリプトなるものを本記事で紹介したいと思います。
提出までの流れ
提出は
- ユーザ認証
- 該当課題の提出フォームを取得
- 提出フォームに提出物を添付し提出ボタンを押す
というよくある流れ。
まず講義ページにいき該当課題のログインページを開きユーザIDとパスワードを入力後、提出フォームがでてくる。 提出フォームにはセレクトボックスが10個程度あって、提出するファイルを一つずつ選択してセットする必要がある。 さらに、提出フォームの下側に「お友達の課題をコピーしてない?」「必須課題全部やった??」などの必要性皆無なチェックボックスが4つあり、 これにチェックを入れないと提出できない仕組みになっている。ここまで約1分。
提出シーケンスの調査
まずはブラウザの開発者ツールやWiresharkなりを駆使してどんな通信が行われているかを調べる。
すると、課題提出では「edu2.cs.inf.shizuoka.ac.jp」のドメインを持つサーバ上に置いてある 3つのプログラム(auth、make-report、submit-report)と通信が行われていることが判明。
edu2.cs.inf.shizuoka.ac.jp - /cgi-bin/auth - /cgi-bin/make-report.cgi?year=〇〇&class=△ △ &report=□ □ - /cgi-bin/submit-report.cgi
ログインボタンを押すとauthでユーザ認証がなされ、 make-reportではログインに成功した場合に該当課題の提出フォームが生成され、 提出フォームの提出ボタンが押されるとsubmit-reportが呼び出され課題の受信処理が行われる。
WebブラウザとWebサーバの間で行われているこの通信を自分で発生させさせることができればオッケー。
HTTP及びTCPについて
作るものは明白で、Webブラウザの代わりに課題を提出するWebクライアントを作ればいい。 となるとHTTPとTCPについて少しばかり知っておく必要がある。
この辺りのプロトコルの詳細は調べれば大抵のことはわかるので割愛。 簡潔に書くと、
ようにしてHTTP通信が行われています。
例えば皆さんが日常で使用しているWEBブラウザだと、「http://hogehoge.com/〜」のようにURLを入力するとそのページがブラウザ上に表示されるかと思いますが、 ブラウザの裏側ではブラウザと要求するページを所持しているサーバとの間でTCPセッションが開始され、HTTPリクエストを発行してHTMLなどのページ情報を取得し、ブラウザで表示しています。
作ろうとしている課題提出プログラムでは、提出フォームの取得、認証情報や添付ファイルの送信でHTTP通信が行われます。
実装
言語は何でもいいのだけど、とりあえず自分の興味もあってPythonで書くことに。 嬉しいことにPythonはライブラリが豊富なので、自身でプロトコルのフレーム構造に従って送信データを作る必要がありません。ここでは、requestというライブラリを使ってHTTP通信を行わせることにします。
作成したプログラム(主要な部分のみ抜粋)
# main script ---------------------------------------------------------------- prefix_url = 'http://edu2.cs.inf.shizuoka.ac.jp' # HTTPセッションを行うための初期化 session = requests.Session() # authに渡すパラメータ param = { 'username': 'csxxxxx', 'password': 'hongeipii', 'mode': 'make-report', 'year': '2017', 'class': 'X151', 'report': sys.argv[1], } # 認証 url = prefix_url+'/cgi-bin/auth.cgi' print('HTTP POST',url[len(prefix_url):],'in the session') resp = session.post(url, data=param, allow_redirects=True) print_result(resp) # 該当課題の提出フォームを取得 url = prefix_url+'/cgi-bin/make-report.cgi?'\ 'year='+param['year']+'&'\ 'amp;class='+param['class']+'&'\ 'amp;report='+param['report'] print('HTTP GET',url[len(prefix_url):],'in the session') resp = session.get(url) print_result(resp) # 提出物データの読み込み form_data = { 'year': param['year'], 'class': param['class'], 'report': param['report'], } print('\n===>','make submit-data...') form_data.update(rep_file) form_data.update(src_files) form_data.update(etc_files) print('\n===>','verify form-data...') pprint(form_data) # 提出 url = prefix_url+'/cgi-bin/submit-report.cgi' print('HTTP POST',url[len(prefix_url):],'in the session') resp = session.post(url, files=form_data) print_result(resp)
解説
HTTPセッション
request.session()によりHTTPセッションを行えるようrequestが初期化されます。(HTTPセッション=HTTP通信を行うためのTCPセッション) 基本的にはリクエスト毎にTCPセッションが確立されるんだけど、そのせいで誰がリクエストを送信したかの情報を知ることができません。 単純にWebページを要求するだけならその情報は必要ないけど、ログイン後にユーザごとに異なる内容のページを受信したいときがあります。(amazon等通販サイトを想像してみてね。) requestではcookieを保持してリクエストを発行するときに一緒に送信するようにしているため、サーバ側ではデータの送信主を区別することができます。
認証
paramで指定した情報をsession.postで送信し、サーバ側ではauthによって受信データを元にログイン処理が行れます。 ※ ユーザ名とパスワード以外の情報はログインページのHTMLに書かれていたのでそれを指定。
提出フォームの取得
課題を指定するための情報をURIに付加してsession.getを実行し、HTTPリクエストを発行。 ※ 書いてて気付いたけど、Webブラウザで操作するためのファイル選択ページを要求しているのでこの部分のプログラムは要らないかも。
提出
提出物をプログラムに読み込み、session.postでHTTPリクエストを発行。 サーバ側ではsubmit-reportにより提出物が受信される。(提出完了!)
おわりに
- 締切ギリギリでも間に合うように課題提出スクリプトなるものを作った
- 課題提出プログラムを作り終えたのが講義終盤であまり恩恵を受けれなかったのが悲しい
- 何故か提出ページが閉鎖されてたので実行結果がないのはお許しください
- Python便利