Python填寫問卷星
有些無意義的問卷作業(yè),真的沒有必要。
但是作業(yè)既然有了,那就想辦法解決它把。
關(guān)于IP限制的問題,可以使用X-Forwarded-For更換IP。
只會用印象筆記markdown的我,感覺簡書markdown好復(fù)雜。
我想做個目錄,但是[toc]沒反應(yīng),...
1.分析接口


1$2:表示選擇第一問的第二個選項(xiàng),這是單選題的數(shù)據(jù)格式
2.構(gòu)造url
構(gòu)造url需要submittype, curID, t, starttime, ktimes, rn, hlv, jqnonce, jqsign
經(jīng)實(shí)驗(yàn),必要的參數(shù)如下:
- submittype,curID,ktimes,rn,jqnonce,jqsign
但是只傳這些參數(shù),會讓你輸入驗(yàn)證碼,最后得出結(jié)果,除了hlv其他參數(shù)都是必要的
必要參數(shù)獲取方式:
- submittype:我做的是調(diào)查類問卷,固定值為1,其他類型問卷沒試過
- curlID:問卷網(wǎng)址中含有此數(shù)值,也可以從訪問問卷網(wǎng)址獲取的response中獲取
- t:時間戳,后三位為隨機(jī)數(shù),推測為提交數(shù)據(jù)的時間,我們可以使用python生成
- starttime:字符串格式的時間,推測為打開網(wǎng)頁的時間,我們可以使用python生成
- ktimes:做題的時間,但是不是秒數(shù),根據(jù)做題時間變長而增大,用以生成jasign,我們可以用python生成
- rn:從訪問問卷網(wǎng)址獲取的response中獲取
- jqnonce: 從訪問問卷網(wǎng)址獲取的response中獲取,用以生成jqsign
- jqsign:使用ktimes和jqnonce
- 經(jīng)過查看js代碼,找到了jqsign的生成函數(shù)
其中a是jqnonce
轉(zhuǎn)換為python:dataenc(a) { var b = ktimes % 10; b == 0 && (b = 1); for (var d = [], c = 0; c < a.length; c++) { var f = a.charCodeAt(c) ^ b; d.push(String.fromCharCode(f)) } return d.join("") }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)
- 經(jīng)過查看js代碼,找到了jqsign的生成函數(shù)
思路有了,可以開工了。
先定義一個問卷星的類:
- wj_url:要填寫的問卷的url
- post_url:用來提交數(shù)據(jù)的url
- header:請求頭
- cookie:請求使用的cookie,提交問卷cookie可設(shè)置可不設(shè)置
- data:需要提交的數(shù)據(jù)
import requests
import re
import time
import random
class WenJuanXing:
def __init__(self, url):
"""
:param url:要填寫的問卷的url
"""
self.wj_url = url
self.post_url = None
self.header = None
self.cookie = None
self.data = None
ktimes生成函數(shù):
def get_ktimes(self):
"""
隨機(jī)生成一個ktimes,ktimes是構(gòu)造post_url需要的參數(shù),為一個整數(shù)
:return:
"""
return random.randint(5, 18)
header設(shè)置函數(shù):
隨機(jī)生成IP,如果不換IP,提交多了會提示輸入驗(yàn)證碼
很少見到x-forwarded-for能有用的時候,問卷星剛好就可以
def set_header(self):
"""
隨機(jī)生成ip,設(shè)置X-Forwarded-For
ip需要控制ip段,不然生成的大部分是國外的
:return:
"""
ip = '{}.{}.{}.{}'.format(112, random.randint(64, 68), random.randint(0, 255), random.randint(0, 255))
self.header = {
'X-Forwarded-For': ip,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko\
) Chrome/71.0.3578.98 Safari/537.36',
}
訪問問卷網(wǎng)址,獲取response:
def get_response(self):
"""
訪問問卷網(wǎng)頁,獲取網(wǎng)頁代碼
:return: get請求返回的response
"""
response = requests.get(url=self.wj_url, headers=self.header)
self.cookie = response.cookies
return response
通過上面的response獲取jqnonce:
由于jqnonce是script里面的一個變量,所以使用正則表達(dá)式提取
我更喜歡xpath,但是好像xpath提不出來
def get_jqnonce(self, response):
"""
通過正則表達(dá)式找出jqnonce,jqnonce是構(gòu)造post_url需要的參數(shù)
:param response: 訪問問卷網(wǎng)頁,返回的reaponse
:return: 找到的jqnonce
"""
jqnonce = re.search(r'.{8}-.{4}-.{4}-.{4}-.{12}', response.text)
return jqnonce.group()
通過response獲取rn:
def get_rn(self, response):
"""
通過正則表達(dá)式找出rn,rn是構(gòu)造post_url需要的參數(shù)
:param response: 訪問問卷網(wǎng)頁,返回的reaponse
:return: 找到的rn
"""
rn = re.search(r'\d{9,10}\.\d{8}', response.text)
return rn.group()
通過response獲取id
def get_id(self, response):
"""
通過正則表達(dá)式找出問卷id,問卷是構(gòu)造post_url需要的參數(shù)
:param response: 訪問問卷網(wǎng)頁,返回的reaponse
:return: 找到的問卷id
"""
id = re.search(r'\d{8}', response.text)
return id.group()
通過jqnonce和ktimes生成jqsign:
def get_jqsign(self, ktimes, jqnonce):
"""
通過ktimes和jqnonce計(jì)算jqsign,jqsign是構(gòu)造post_url需要的參數(shù)
: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)
通過response獲取starttime:
獲取starttime的正則表達(dá)式我沒有獲取秒,因?yàn)楂@取秒后會彈驗(yàn)證碼
我猜測:
- starttime,t, ktimes存在一定關(guān)系,但這些數(shù)據(jù)都是我們偽造的,忽略的秒數(shù)可能會消除我們偽造的虛假
def get_start_time(self, response):
"""
通過正則表達(dá)式找出問卷starttime,問卷是構(gòu)造post_url需要的參數(shù)
:param response: 訪問問卷網(wǎng)頁,返回的reaponse
:return: 找到的starttime
"""
start_time = re.search(r'\d+?/\d+?/\d+?\s\d+?:\d{2}', response.text)
return start_time.group()
終于把各個參數(shù)獲取的差不多了,可以生成url了:
def set_post_url(self):
"""
生成post_url
:return:
"""
self.set_header() # 設(shè)置請求頭,更換ip
response = self.get_response() # 訪問問卷網(wǎng)頁,獲取response
ktimes = self.get_ktimes() # 獲取ktimes
jqnonce = self.get_jqnonce(response) # 獲取jqnonce
rn = self.get_rn(response) # 獲取rn
id = self.get_id(response) # 獲取問卷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)) # 生成一個時間戳,最后三位為隨機(jī)數(shù)
url = 'https://www.wjx.cn/joinnew/processjq.ashx?submittype=1&curID={}&t={}&starttim' \
'e={}&ktimes={}&rn={}&jqnonce={}&jqsign={}'.format(id, time_stamp, start_time, ktimes, rn, jqnonce, jqsign)
self.post_url = url # 設(shè)置url
print(self.post_url)
3.生成數(shù)據(jù)
url生成好了,我們基本成功了,剩下的工作就比較輕松了
生成需要提交的數(shù)據(jù):
- 之前提過了,1$1代表選擇第一問的第一個選項(xiàng)
- 如果有更多的選項(xiàng)和不同的問題類型,需要瀏覽器抓包了解其結(jié)構(gòu)
- 可以使用有限制的隨機(jī)函數(shù),是提交的結(jié)果不那么假
- 我這兒只有一道題作為測試,所以數(shù)據(jù)構(gòu)造的很簡答
def set_data(self):
"""
這個函數(shù)中生成問卷的結(jié)果,可根據(jù)問卷結(jié)果,隨機(jī)生成答案
:return:
"""
self.data = {
'submitdata': '1$1'
}
4.提交數(shù)據(jù)
萬事俱備,只欠東風(fēng)。
如果response.text為22,則代表失敗
會有少數(shù)的失敗,不知道什么原因
post_data函數(shù):
def post_data(self):
"""
發(fā)送數(shù)據(jù)給服務(wù)器
:return: 服務(wù)器返回的結(jié)果
"""
self.set_data()
response = requests.post(url=self.post_url, data=self.data, headers=self.header, cookies=self.cookie)
return response
run函數(shù):
至此我們就可以全自動化的填問卷了,而且不受ip的限制
def run(self):
"""
填寫一次問卷
:return:
"""
self.set_post_url()
result = self.post_data()
print(result.content.decode())
一次填寫多個問卷:
def mul_run(self, n):
"""
填寫多次問卷
:return:
"""
for i in range(n):
self.run()
5.啟動啟動
運(yùn)行一下:
if __name__ == '__main__':
w = WenJuanXing('https://www.wjx.cn/jq/xxxxxxxx.aspx')
w.mul_run(100)
結(jié)果:
紅色圈出來的22代表失敗,其他的代表成功

問卷統(tǒng)計(jì):

地理位置:

6.完整代碼
好了,該睡覺了??
如果覺得速度不夠快,可以用多線程
import requests
import re
import time
import random
class WenJuanXing:
def __init__(self, url):
"""
:param url:要填寫的問卷的url
"""
self.wj_url = url
self.post_url = None
self.header = None
self.cookie = None
self.data = None
def set_data(self):
"""
這個函數(shù)中生成問卷的結(jié)果,可根據(jù)問卷結(jié)果,隨機(jī)生成答案
:return:
"""
self.data = {
'submitdata': '1$1'
}
def set_header(self):
"""
隨機(jī)生成ip,設(shè)置X-Forwarded-For
ip需要控制ip段,不然生成的大部分是國外的
:return:
"""
ip = '{}.{}.{}.{}'.format(112, random.randint(64, 68), random.randint(0, 255), random.randint(0, 255))
self.header = {
'X-Forwarded-For': ip,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko\
) Chrome/71.0.3578.98 Safari/537.36',
}
def get_ktimes(self):
"""
隨機(jī)生成一個ktimes,ktimes是構(gòu)造post_url需要的參數(shù),為一個整數(shù)
:return:
"""
return random.randint(15, 50)
def get_response(self):
"""
訪問問卷網(wǎng)頁,獲取網(wǎng)頁代碼
:return: get請求返回的response
"""
response = requests.get(url=self.wj_url, headers=self.header)
self.cookie = response.cookies
return response
def get_jqnonce(self, response):
"""
通過正則表達(dá)式找出jqnonce,jqnonce是構(gòu)造post_url需要的參數(shù)
:param response: 訪問問卷網(wǎng)頁,返回的reaponse
:return: 找到的jqnonce
"""
jqnonce = re.search(r'.{8}-.{4}-.{4}-.{4}-.{12}', response.text)
return jqnonce.group()
def get_rn(self, response):
"""
通過正則表達(dá)式找出rn,rn是構(gòu)造post_url需要的參數(shù)
:param response: 訪問問卷網(wǎng)頁,返回的reaponse
:return: 找到的rn
"""
rn = re.search(r'\d{9,10}\.\d{8}', response.text)
return rn.group()
def get_id(self, response):
"""
通過正則表達(dá)式找出問卷id,問卷是構(gòu)造post_url需要的參數(shù)
:param response: 訪問問卷網(wǎng)頁,返回的reaponse
:return: 找到的問卷id
"""
id = re.search(r'\d{8}', response.text)
return id.group()
def get_jqsign(self, ktimes, jqnonce):
"""
通過ktimes和jqnonce計(jì)算jqsign,jqsign是構(gòu)造post_url需要的參數(shù)
: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):
"""
通過正則表達(dá)式找出問卷starttime,問卷是構(gòu)造post_url需要的參數(shù)
:param response: 訪問問卷網(wǎng)頁,返回的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() # 設(shè)置請求頭,更換ip
response = self.get_response() # 訪問問卷網(wǎng)頁,獲取response
ktimes = self.get_ktimes() # 獲取ktimes
jqnonce = self.get_jqnonce(response) # 獲取jqnonce
rn = self.get_rn(response) # 獲取rn
id = self.get_id(response) # 獲取問卷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)) # 生成一個時間戳,最后三位為隨機(jī)數(shù)
url = 'https://www.wjx.cn/joinnew/processjq.ashx?submittype=1&curID={}&t={}&starttim' \
'e={}&ktimes={}&rn={}&jqnonce={}&jqsign={}'.format(id, time_stamp, start_time, ktimes, rn, jqnonce, jqsign)
self.post_url = url # 設(shè)置url
print(self.post_url)
def post_data(self):
"""
發(fā)送數(shù)據(jù)給服務(wù)器
:return: 服務(wù)器返回的結(jié)果
"""
self.set_data()
response = requests.post(url=self.post_url, data=self.data, headers=self.header, cookies=self.cookie)
return response
def run(self):
"""
填寫一次問卷
:return:
"""
self.set_post_url()
result = self.post_data()
print(result.content.decode())
def mul_run(self, n):
"""
填寫多次問卷
:return:
"""
for i in range(n):
time.sleep(0.1)
self.run()
if __name__ == '__main__':
w = WenJuanXing('url')
w.mul_run(100)