*一、前言
**
笔者在家闲得无聊,突然想突破一下问卷星的反爬虫机制,顺便刷刷问卷,于是就开始分析了。
**
二、分析过程
**
1、fiddler抓包
模拟提交首先当然是打开fiddler看看提交了什么包啦。
2、分析不变参数
我们先来看不变的参数,这样的话我们把它作为一个常量就可以了。经过多次提交之后发现,submittype(答卷类型?),curID(答卷ID),rn(一个随机数random,但是在同一份问卷中是固定的),hlv(这个猜不出来)
3、分析变量
1、t参数
提交结束时间,因为这个时间总是比starttime晚,而且转化为时间戳就是一个具体的时间。
2、starttime参数
开始时间,见名知意,调查问卷也统计这个参数
3、ktimes
这个一开始也是百思不得其解,后来在提交按钮的Event Listeners的click事件中找到了ktimes关键字
在这个js文件中,ktimes被做了多次自增运算,我怀疑就是故意来坑刷题的人。(▼ヘ▼#),但是最后我直接放了一个随机数好像就蒙混过关了。。。
4、jqnonce参数
这个参数在问卷页面直接搜索关键字直接可以搜到,利用正则表达式截取就可以了。
5、jqsign参数
我更加加深了我的怀疑,这个参数也是故意来坑刷题的人的。他先是做了代码混淆,然后又做了一个莫名其妙的运算。。不过还好我为什么要和他硬碰硬呢?直接利用python库执行js代码,绕开他的签名算法。
三、网站的反爬措施
暂时知道的有两个吧,一个是单IP提交30次左右直接标记为可疑IP,填问卷的时候疯狂弹验证码,解决这个问题我用了代理IP。另一个是不同IP20秒内提交11次左右问卷被标记为可疑问卷,此时所有填写IP都会疯狂弹验证码,解决这个问题我用了程序sleep的方式来使问卷退出黑名单。
四、代码如下
import random
import time
import requests
import urllib3
import execjs
import reurllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
requests.adapters.DEFAULT_RETRIES = 5
TARGET_URL = 'https://www.wjx.cn/jq/***.aspx'
headers = {'Connection': 'keep-alive','Content-Length': '24','Origin': 'https://www.wjx.cn','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36','Content-Type': 'application/x-www-form-urlencoded','Accept': '*/*','Referer': TARGET_URL,'Accept-Encoding': 'gzip, deflate, br','Accept-Language': 'zh-CN,zh;q=0.9','Cookie':'*****'
}# 代理服务器
proxies = {"http": proxyMeta,"https": proxyMeta,
}main_session = requests.session()
def get_jqnonce():response = requests.get(TARGET_URL, verify=False, timeout=5)html_text = response.textjq_list = re.findall('jqnonce.*";', html_text)jq_value = jq_list[0].split('"')time_list = re.findall('starttime.*";', html_text)time_value = time_list[0].split('"')return jq_value[1], time_value[1]def get_jqsign(jqnonce, ktimes):jsencrpt = """function dataenc(a,ktimes) {var c, d, e, b = ktimes % 10;for (0 == b && (b = 1), c = [], d = 0; d < a.length; d++) e = a.charCodeAt(d) ^ b, c.push(String.fromCharCode(e));return c.join("")}"""jsfunc = execjs.compile(jsencrpt)jqsign = jsfunc.call('dataenc', jqnonce, ktimes)return jqsignsubmittype = '1'
curID = re.findall('/[0-9]+', TARGET_URL)[0].replace('/', '')
(jqnonce, starttime) = get_jqnonce()
timeArray = time.strptime(starttime, "%Y/%m/%d %H:%M:%S")
rn = '1739566547'
hlv = '1'
print('--------------开始提交--------------------')
for i in range(0, 100):t = str(int(time.mktime(timeArray)) + random.randint(5, 30)) + str(random.randint(100, 999)) #模拟作答时间,同时补齐时间戳ktimes = random.randint(10, 100)jqsign = get_jqsign(jqnonce, ktimes)payload = {'submittype': submittype,'curID': curID,'starttime': starttime,'t': t,'rn': rn,'hlv': hlv,'ktimes': ktimes,'jqnonce': jqnonce,'jqsign': jqsign}submitdata = {'submitdata': '1$' + str(random.randint(1, 2)) + '}2$' + str(random.randint(1, 2))} #作答结果随机response = main_session.post('https://www.wjx.cn/joinnew/processjq.ashx', verify=False, params=payload,data=submitdata,headers=headers, proxies=proxies)main_session.keep_alive = Falseprint(response.text)if re.findall('^7', response.text):print('发现验证码,我要睡一会')time.sleep(20)print('我醒了')print(f'提交到第{i}次')
print('--------------提交结束--------------------')
五、存在问题
首先还是效率比较慢,因为要长时间sleep而且没有采取协程或者多线程,还有就是存在一个严重的问题,header必须强制加入cookie否则会报错误,这个我很纳闷,我之前用第一次请求来传cookie竟然失效了,这个问题希望大佬可以解决一下。以及有其他问题都可以指出来,欢迎技术交流。