Google Form自動回答Botでクソアンケートに勝つ(Python3)
ある時、僕の学校にあるGoogle Formで作られたアンケートに答えるよう手紙が来ました。
見てみると結構長め、さらには回答必須という地獄のような物でした。やるのは面倒だし回答はしなきゃダメ……
そうだ、自動化すればいいじゃないか!
「退屈なことはPythonにやらせよう」
というわけで結構前にGoogle Formに自動で回答を送信するBot(不完全)を作ったので備忘録程度に書きます。
といっても先人にGoogle Form回答自動化してる方がいらっしゃったのでそれを参考にカチカチとやるだけでした。先人に感謝。
Google FormのURL
最初はURLがhttps://docs.google.com/forms/d/e/1FAIpQLSc7J4XPNKYodJlKDeDzfNJHlEGSFbSWqzxhgORDm0ODQWj0Tw/viewformのように最後にviewform
がくっついてます。で、情報を送信するとhttps://docs.google.com/forms/d/e/1FAIpQLSc7J4XPNKYodJlKDeDzfNJHlEGSFbSWqzxhgORDm0ODQWj0Tw/formResponseとなり、最後にviewform
の代わりにformResponse
がくっつきます。
また、Google Formでは回答の送信をURLパラメータを使って行っています。
URLパラメータとは
GETメソッドでサーバーとやりとりする際に、URLの後ろにくっついてる?以降のやつです。「クエリ文字列」とも言います。サーバーにデータを送る際、もうURLに載せちゃおうかということで送信データはリクエストURLの後ろに付けられます(http://example.com?hoge=fuga
とか)。?の後に「名前=値」のような形式で記述します。さっきの例では名前がhogeで値がfugaです。
あとパラメータが「結果に影響を与える外部からのデータ」という意味を持ってたりします。
やること
Google Formさんはフォームの情報を?
の後に色々つけて送信しているってことは、プログラム側でそのURLを再現してあげればOKです。URLを作ってそれを送信するのはPythonに、パラメータキーや送信する値はjsonに書いていきます。
そのためには、Google FormのURLパラメータの取得が必要です。取得していきます。
ちなみに、こっから作ったTestのGoogle Formを元にやっていきます。URL
URLパラメータの取得
Chromeの場合は、Ctrl + Uでソースコードが見れるので、そっから検索でentryを探してあげて下さい。
HTMLのinputタグのname属性に記載されている内容がパラメータのキーになります。ここでは
name="entry.367274191"
ですね。おそらく生成されるURLは/formResponse?entry.367274191=回答
となります。
.jsonファイル作り
cfg.json
{ "form_url": "https://docs.google.com/forms/d/e/1FAIpQLScHpzwQEFsDSzBxyInrRzxNEVyXqOdDsU5LNDYad87e-JMDgQ/", "entry": { "ans": 367274191 }, "output":{ "ans": "あかんこ" } }
form_url
には送信したいFormのURL、entry
には先程取得したパラメータキーの数字、output
にはentryに対応する答えを送信します。ここでは「あかんこ」か「じゃぱん」のどちらかですね。これらの変数は自由に変更してOKですが、entryの数字とそれに対応する答えの変数(ここではans
)は同じにしましょう。
mainファイル作り
requestsライブラリが必要です。
main.py
#coding: utf-8 import requests import json fname = "cfg.json" with open(fname, "r") as f: cfg = json.load(f) params = {"entry.{}".format(cfg["entry"][k]): cfg["output"][k] for k in cfg["entry"].keys()} res = requests.get(cfg["form_url"] + "formResponse", params=params)
ほぼ先人様のコードの パクリ オマージュです。params
にURLパラメータを入れてrequest.get
でGETリクエストをしてます。うーん便利。送信されてるか不安な人は
if res.status_code == 200: print("Done!") else: res.raise_for_status() print("Error")
のようにステータスコードを見てあげて下さい。
実際リクエストしているURLを見てみます。
print(res.url)
を適宜どっかにつけてやると、
https://docs.google.com/forms/d/e/1FAIpQLScHpzwQEFsDSzBxyInrRzxNEVyXqOdDsU5LNDYad87e-JMDgQ/formResponse?entry.367274191=%E3%81%82%E3%81%8B%E3%82%93%E3%81%93
Google FormのURLの仕組みはこのようになっていたことがわかります。=以降のURLエンコードを直してやると「あかんこ」になるので、ちゃんと/formResponse?entry.367274191=回答
になっています。
Tips
回答なし
選択肢以外の回答
cfg.jsonのoutput部分を選択肢にない回答にしてやったらどうなるのでしょうか。
結論から言うと、回答としてカウントされません。
画像を見てもらうと分かる通り、全体の22件の回答に対し17件の回答しか来ていません。これはダメですね。必須回答なので必ず22件無ければならないはずなのですが…
無回答
cfg.jsonでentryもoutputも何も指定せずにリクエストを送ったらどうなるのでしょうか。すると、以外なことに(?)
https://docs.google.com/forms/d/e/1FAIpQLScHpzwQEFsDSzBxyInrRzxNEVyXqOdDsU5LNDYad87e-JMDgQ/viewform
と、formResponse
ではなくviewform
のリンクをリクエストしています。もちろん回答は送信できていません。
課題
inputタグからname属性をわざわざ探すところが結構面倒なのでWebスクレイピングとか使って自動で収集、あわよくば入力出来るようにしてみたいな〜ぐらいには考えてます。