*一、前言
**
笔者在家闲得无聊,突然想突破一下问卷星的反爬虫机制,顺便刷刷问卷,于是就开始分析了。
**
二、分析过程
**
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 re
urllib3.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.text
jq_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 jqsign
submittype = '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 = False
print(response.text)
if re.findall('^7', response.text):
print('发现验证码,我要睡一会')
time.sleep(20)
print('我醒了')
print(f'提交到第{i}次')
print('--------------提交结束--------------------')
五、存在问题
首先还是效率比较慢,因为要长时间sleep而且没有采取协程或者多线程,还有就是存在一个严重的问题,header必须强制加入cookie否则会报错误,这个我很纳闷,我之前用第一次请求来传cookie竟然失效了,这个问题希望大佬可以解决一下。以及有其他问题都可以指出来,欢迎技术交流。
转载:https://blog.csdn.net/weixin_43913097/article/details/105474161