やること
※本記事は社会通念上繊細な内容を含んでいます。本記事の内容による損害については一切の責任を負いかねます。あらかじめご了承ください。
1月は大学の卒論(卒業論文)シーズンです。
筆者は毎年Twitterで「卒論 アンケート お願い」で検索して、世の大学生がどんな卒論テーマに取り組んでいるかを眺めるのが趣味になっています。消費者の認知や行動心理に関する研究テーマが多いかなと思います。
中には無記名で何度でも回答できるアンケートもあり、万が一回答が集まらなかった場合に備えている様子がうかがえます。「卒業がかかっています!」「拡散お願いします!」といった切実な声も見受けられます。
なんとか力になれないかと思い、今回は、Googleフォームで作成されたアンケートへの自動回答プログラムを作りました。
実行環境
WinPython3.6をおすすめしています。
Google Colaboratoryが利用可能です。
pip
ページのソースコードを扱うためのパッケージをインストールします。
pip install requests
pip install beautifulsoup4
実験用アンケートフォーム
3つの質問からなるアンケートフォームを作りました。
実験1:パラメータの手動取得による自動回答
まずはページのソースコードから必要な情報を手動で取り出し、プログラムで自動回答できるかを試します。
ページのソースコードから質問IDを取得します。「entry.」の後に続く番号です。
質問に対応する選択肢はアンケートページからコピペするか、もしくはソースコードを「-value=”」で検索して見つけます。
これらをプログラムのパラメータ欄に貼り付けて実行します。プログラムは約2秒間隔で10回の回答を行う設定になっています。
import time
import requests
import urllib.parse
import numpy.random as nr
#==================================================
# パラメータ
#==================================================
#GoogleフォームURL(/viewformで終わる)
url = 'https://docs.google.com/forms/d/e/1FAIpQLSeBp-LtVMwoXAd8Tb1pGwVaQ8XkiSw6XXFvRxkwrrkxy8yDKw/viewform'
#質問IDと選択肢
info = {'1323828330':['男性', '女性', '回答しない'],
'694838664':['10代', '20代', '30代', '40代', '50代以上'],
'1544222442':['ある', 'ない']}
#繰り返し回数
num = 10
#インターバル秒
mean_sleep_time = 2
#==================================================
# 自動送信
#==================================================
#urlからviewformを削ってformResponse
url_base = url.replace('viewform', 'formResponse')
#pp_urlを付ける
url_base += '?usp=pp_url'
#繰り返し回答
for i in range(1, num + 1):
print('=====================')
print('Sending {}/{}'.format(i, num))
#
url_send = url_base
#ランダムに選択肢を選ぶ
for entry in info:
ans = nr.choice(info[entry])
url_send += '&entry.' + entry + '='
url_send += urllib.parse.quote(ans)
#送信
print(url_send)
res = requests.get(url_send)
#結果
print(res)
#インターバル
sleep_time = mean_sleep_time * (nr.rand() + 0.5)
print('Sleeping {}s'.format(round(sleep_time, 2)))
time.sleep(sleep_time)
=====================
Sending 1/10
URL https://docs.google.com/forms/d/e/1FAIpQLSeBp-LtVMwoXAd8Tb1pGwVaQ8XkiSw6XXFvRxkwrrkxy8yDKw/formResponse?usp=pp_url&entry.1323828330=%E5%9B%9E%E7%AD%94%E3%81%97%E3%81%AA%E3%81%84&entry.694838664=30%E4%BB%A3&entry.1544222442=%E3%81%82%E3%82%8B
<Response [200]>
Sleeping 2.12s
=====================
Sending 2/10
URL https://docs.google.com/forms/d/e/1FAIpQLSeBp-LtVMwoXAd8Tb1pGwVaQ8XkiSw6XXFvRxkwrrkxy8yDKw/formResponse?usp=pp_url&entry.1323828330=%E7%94%B7%E6%80%A7&entry.694838664=10%E4%BB%A3&entry.1544222442=%E3%81%82%E3%82%8B
<Response [200]>
Sleeping 1.06s
=====================
・
・
・
=====================
Sending 10/10
URL https://docs.google.com/forms/d/e/1FAIpQLSeBp-LtVMwoXAd8Tb1pGwVaQ8XkiSw6XXFvRxkwrrkxy8yDKw/formResponse?usp=pp_url&entry.1323828330=%E5%9B%9E%E7%AD%94%E3%81%97%E3%81%AA%E3%81%84&entry.694838664=20%E4%BB%A3&entry.1544222442=%E3%81%82%E3%82%8B
<Response [200]>
Sleeping 1.81s
うまくいきました!出力されたURLにブラウザからアクセスすると「回答を記録しました」のページが表示され、アンケートの回答数もきちんと増えていました。(現在は防衛用の質問を入れているので送信前の段階が見られます。)
インターバルは設定した時間の50%~150%(今回は1.0~3.0秒)の値をランダムに取ります。日本語の選択肢をURLに乗せて送信する場合の文字エンコードが少し面倒くさかったです。
実験2:パラメータの自動取得による自動回答
動作が確認できれば、あとは全自動を目指すのみです。entry番号と選択肢のコピペが面倒でしたので、ページのソースコードから自動的に取得します。
したがってパラメータはアンケートURL、回数、インターバルだけで済むようになりました。
import time
import requests
import urllib.parse
import numpy.random as nr
from bs4 import BeautifulSoup
#==================================================
# パラメータ
#==================================================
#GoogleフォームURL(/viewformで終わる)
url = 'https://docs.google.com/forms/d/e/1FAIpQLSeBp-LtVMwoXAd8Tb1pGwVaQ8XkiSw6XXFvRxkwrrkxy8yDKw/viewform'
#繰り返し回数
num = 10
#インターバル秒
mean_sleep_time = 2
#==================================================
# パラメータの自動取得
#==================================================
#ページのbodyの取得
html = requests.get(url)
soup = BeautifulSoup(html.content, "html.parser")
body = str(soup.body)
#質問の開始地点で区切る
parts = body.split('entry.')
#格納用
info = {}
#質問で繰り返し
for part in parts[1:]:
#entry番号の取得
entry = part.split('_sentinel')[0]
#選択肢の開始地点で区切る
frames = part.split('-value=\"')
#選択肢で繰り返し
choice = []
for frame in frames[1:]:
#選択肢の取得
values = frame.split('\"')[0]
choice.append(values)
#格納
print('entry={}, choice={}'.format(entry, choice))
info[entry] = choice
#==================================================
# 自動送信
#==================================================
#urlからviewformを削ってformResponse
url_base = url.replace('viewform', 'formResponse')
#pp_urlを付ける
url_base += '?usp=pp_url'
#繰り返し回答
for i in range(1, num + 1):
print('=====================')
print('Sending {}/{}'.format(i, num))
#
url_send = url_base
#ランダムに選択肢を選ぶ
for entry in info:
ans = nr.choice(info[entry])
url_send += '&entry.' + entry + '='
url_send += urllib.parse.quote(ans)
#送信
print(url_send)
res = requests.get(url_send)
#結果
print(res)
#インターバル
sleep_time = mean_sleep_time * (nr.rand() + 0.5)
print('Sleeping {}s'.format(round(sleep_time, 2)))
time.sleep(sleep_time)
できました!
インターバルを短くして実行したらあっという間に4000回答を超えました。
ちなみに性別の回答数が少ないのは、実験1の段階で「回答しない」を「どちらでもない」と誤記入してしまっていたためです。選択肢を間違えても回答は送信されますが記録はされないようです。実験2のコードであればそんなミスは起きないと思います。
このプログラムは現状「ラジオボタン」「チェックボックス」に対応しています。「記述式」「プルダウン」等には対応していません。また、セクション区切り(改ページ)のあるアンケートにも対応していません。
まとめ
1月は卒論の提出〆切りが迫り、1日でも早くデータを集めないといけない状況です。「もうどうしてもやばいです!あと2時間で10万人の回答を集めないといけないんです!」といった依頼があればこのプログラムでたくさん回答しておきますので、遠慮なくお声がけください。
雑記
今回は少し風刺的な内容になってしまいました。このコードを公開するかどうか一週間くらい考えていましたが、まあ問題なかろうと判断しました。例えば、フォロワー数100の人がアンケートを作成してTwitterで「拡散希望」したとします。90件の回答が集まったら「うまくいった」と思うでしょう。では900万件の回答が集まったら「これはおかしい」と思うでしょうか?本当に900万人が回答したかもしれません。
そう考えるとbotでも問題なさそうです。すでに内定もあるでしょうし、効率良く卒論が仕上がるといいですね。