问卷星是一个常用的在线问卷系统,为了减少自己每天手填问卷的麻烦(大嘘),对着网上的代码抄抄改改做了这个东西
参考https://www.jianshu.com/p/8ee4cb256159 这篇文章的代码,做了一些小改动

  • 问卷ID直接从url里获得(实际上是用ID拼出url),而不是在HTML源代码里扒出来,因为问卷ID不一定是8位
  • 适当的减缓提交速度,希望可以减少验证码
  • 无限重试到提交成功为止,适合定时签到一类的情况

问卷星提交以后会返回一串奇怪的东西,提交成功的情况下是这样
10〒/wjx/join/complete.aspx?q=********&JoinID=********&jidx=****&tvd=********
然后跳转到/wjx/join/complete.aspx这个页面。
测试中出现了提交失败的情况,大部分是因为验证码这个万恶之源:
22〒请输入验证码
9〒0〒您提交的答案不符合要求,请检查并修改后重新提交!
7〒您输入的验证码有误,请重新输入!
前面的数字是返回码,只有10是成功的状态
其实只要检查返回中有没有complete这个字符串就可以判断是否成功

另外,问卷星提交答案的格式是这样的:
题号$答案[[^答案][^答案]……][}题号$答案][}题号$答案]……
即$分割题号与答案,^分割同一题目的多个答案,}分割不同题目

最后上修改过的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# -*- coding: utf-8 -*-
import requests
import re
import time
import random
import sys
import datetime

class WenJuanXing:
def __init__(self, id):
"""
:param url:要填写的问卷的url
"""
self.wj_url = 'http://www.wjx.top/m/' + id + '.aspx'
self.wj_id = id
self.post_url = None
self.header = None
self.cookie = None
self.data = None

def set_data(self):
"""
这个函数中生成问卷的结果,可根据问卷结果,随机生成答案
:return:
"""
# 改掉这里的数据
self.data = {
'submitdata': '1$1145141919}2$' + datetime.datetime.now().strftime('%Y-%m-%d') + \
'}3$11^' + str(random.randint(10, 59)) + '}4$36.' + str(random.randint(0, 2))
}

def set_header(self):
"""
随机生成ip,设置X-Forwarded-For
ip需要控制ip段,不然生成的大部分是国外的
:return:
"""
ip = '{}.{}.{}.{}'.format(112, random.randint(64, 68), random.randint(0, 255), random.randint(0, 255))
self.header = {
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'Referer': self.wj_url,
'User-Agent': 'Mozilla/5.0 (Linux; U; Android 4.3; zh-cn; YL-Coolpad 5892_C00 Build/JLS36C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
'X-Forwarded-For': ip
}

def get_ktimes(self):
"""
随机生成一个ktimes,ktimes是构造post_url需要的参数,为一个整数
:return:
"""
return random.randint(100, 200)

def get_response(self):
"""
访问问卷网页,获取网页代码
:return: get请求返回的response
"""
print("Try to f**k " + self.wj_url)
response = requests.get(url=self.wj_url, headers=self.header, verify=False)
self.cookie = response.cookies
return response

def get_jqnonce(self, response):
"""
通过正则表达式找出jqnonce,jqnonce是构造post_url需要的参数
:param response: 访问问卷网页,返回的reaponse
:return: 找到的jqnonce
"""
jqnonce = re.search(r'.{8}-.{4}-.{4}-.{4}-.{12}', response.text)
return jqnonce.group()

def get_rn(self, response):
"""
通过正则表达式找出rn,rn是构造post_url需要的参数
:param response: 访问问卷网页,返回的reaponse
:return: 找到的rn
"""
rn = re.search(r'\d{9,10}\.\d{8}', response.text)
return rn.group()

def get_id(self, response):
"""
通过正则表达式找出问卷id,问卷是构造post_url需要的参数
:param response: 访问问卷网页,返回的reaponse
:return: 找到的问卷id
"""
id = re.search(r'\d{8}', response.text)
return id.group()

def get_jqsign(self, ktimes, jqnonce):
"""
通过ktimes和jqnonce计算jqsign,jqsign是构造post_url需要的参数
:param ktimes: ktimes
:param jqnonce: jqnonce
:return: 生成的jqsign
"""
result = []
b = ktimes % 10
if b == 0:
b = 1
for char in list(jqnonce):
f = ord(char) ^ b
result.append(chr(f))
return ''.join(result)

def get_start_time(self, response):
"""
通过正则表达式找出问卷starttime,问卷是构造post_url需要的参数
:param response: 访问问卷网页,返回的reaponse
:return: 找到的starttime
"""
start_time = re.search(r'\d+?/\d+?/\d+?\s\d+?:\d{2}', response.text)
return start_time.group()

def set_post_url(self):
"""
生成post_url
:return:
"""
self.set_header() # 设置请求头,更换ip
response = self.get_response() # 访问问卷网页,获取response
ktimes = self.get_ktimes() # 获取ktimes
jqnonce = self.get_jqnonce(response) # 获取jqnonce
rn = self.get_rn(response) # 获取rn
id = self.wj_id # 获取问卷id
jqsign = self.get_jqsign(ktimes, jqnonce) # 生成jqsign
start_time = self.get_start_time(response) # 获取starttime
time_stamp = '{}{}'.format(int(time.time()), random.randint(100, 200)) # 生成一个时间戳,最后三位为随机数
url = 'https://www.wjx.cn/joinnew/processjq.ashx?submittype=1&source=directphone&curID={}&t={}&starttim' \
'e={}&ktimes={}&rn={}&hlv=1&jqnonce={}&jqsign={}'.format(id, time_stamp, start_time, ktimes, rn, jqnonce, jqsign)
self.post_url = url # 设置url
print(self.post_url)

def post_data(self):
"""
发送数据给服务器
:return: 服务器返回的结果
"""
self.set_data()
print("F**king " + self.wj_id + " with " + self.data['submitdata'])
response = requests.post(url=self.post_url, data=self.data, headers=self.header, cookies=self.cookie, verify=False)
return response

def run(self):
"""
填写一次问卷
:return:
"""
self.set_post_url()
print("Zzz...")
# 等待一会再提交,减少出验证码的概率
time.sleep(random.randint(10, 20))
print("Go Ahead!")
result = self.post_data()
print(result.text)
return result.text

def mul_run(self, n):
"""
填写多次问卷
:return:
"""
for i in range(n):
time.sleep(60)
self.run()


if __name__ == '__main__':
# 改成自己的问卷ID
# http://www.wjx.top/m/{这就是ID}.aspx
w = WenJuanXing("这里改掉")
while(w):
r = w.run()
if r.find("complete")>=0 :
print("Yattaze!")
break
print("Oh s**t, submit failed. Let's try again later...")
time.sleep(random.randint(60, 120))

挂机运行效果(别学我总用root登录……)
qqqq.PNG