一.签名反爬虫:
签名是根据数据源进行计算或加密的过程,签名的结果是一个具有唯一性和一致性的字符串。签名结果的特性成为数据来源和数据完整的条件,可以有效避免服务器端将伪造的数据或被篡改的数据当成正常数据处理。
二.案例分析
(1).准备工作:
网址:http://www.porters.vip/verify/sign
任务:爬取旅游公告页面详情信息
(2).分析:
点击‘点击查看详情’,就是公告详情
按F12,查看NetWork,分析数据从什么地方读取的。
(其中md5.js和sign.js保存下来方便使用)
点击sign.js进行分析
// fetch():鼠标点击事件后激活,使用GET方法向目标URL发起网络请求,并将覆盖content;使用了Ajax请求后端API
function fetch(){
text=$.ajax({
type:"GET", async: false,
// 结合Tornado框架使用进行验证交给后端API处理
url:"http://localhost:8000/fet/" + uri()
});
$("#content").html(text.responseText);
}
function randints(r, n, tof){
/* 生成随机数字,tof决定返回number类型或者字符串类型
r 代表数字范围 n 代表数量
*/
var result = [];
if(tof){
return Math.floor(Math.random()*r);
}
for(var i=0;i<n;i++){
s = Math.floor(Math.random()*r);
result.push(s);
}
return result.join('');
}
function randstrs(n){
// 生成随机字母,n为随机字母的数量
var result = [];
for(var i=0; i<n; i++){
s = String.fromCharCode(65+randints(25, 1, 1));
result.push(s);
}
return result.join('');
}
function uri(){
// 生成随机5个小于9的数字
var action = randints(9, 5, 0);
// 生成时间戳
var tim = Math.round(new Date().getTime()/1000).toString();
// 生个5个随机的大写字母
var randstr = randstrs(5);
// 拼接
var hexs = hex_md5(action+tim+randstr);
args = '?actions=' + action + '&tim=' + tim + '&randstr=' + randstr + '&sign=' + hexs;
return args;
}
三.Python代码实现
(1).新建项目文件夹signature,两个子文件夹static, templates
(2).static下有md5.js和sign.js这两个文件
(3).templates下有index.html模板(查看网页源代码直接保存)
代码演示:
# coding:utf-8
import os
import hashlib
from tornado.web import RequestHandler, Application
from tornado.ioloop import IOLoop
from time import time
from datetime import datetime
class IndexHandler(RequestHandler):
def get(self, *args, **kwargs):
self.render('index.html')
def make_app():
return Application(
[(r'/',IndexHandler)],
template_path=os.path.join(os.getcwd(), 'templates'),
static_path=os.path.join(os.getcwd(), 'static'),
)
if __name__ == '__main__':
app = make_app()
app.listen(8000)
IOLoop.current().start()
测试一下:
(4).在浏览器输入http://localhost:8000 测试成功。
完整代码:
# coding:utf-8
import os
import hashlib
from tornado.web import RequestHandler, Application
from tornado.ioloop import IOLoop
from time import time
from datetime import datetime
class IndexHandler(RequestHandler):
def get(self, *args, **kwargs):
self.render('index.html')
class FetHandler(RequestHandler):
content = """
<p>参团的游客,应听从领队、导游人员的安全提醒,切莫擅自行动。
自身的人身、财物安全要注意,购买人身意外险,贵重物品要随身携带,
不要留在车内或者交由他人保管。参加漂流、摩天轮等高风险项目的时候,
要认真听从工作人员的安排,切莫求刺激而发生意外。</p>
<p>以下是本次参团出行需要遵守的规范要求:</p>
<p>一、跟刺激相比,命更重要,没有命就什么都没了。</p>
<p>二、旅行中会遇到很多你从未见过的植物和动物,不要轻易打扰它们,有可能有毒。</p>
<p>三、身体感觉不适,尤其是发烧、乏力和呕吐等情况必须报告随队医护人员。</p>
<p>四、出发前请跟家人沟通好,避免造成失联错觉。</p>
<p>五、出发前请按照队长的要求准备好必备衣物和干粮,最重要的是水。</p>
<p>六、旅行途中必须紧跟队伍,不许在无人知晓的情况下行动。</p>
<p>七、如不慎走失,请先释放信号弹,半小时后无人联系再想办法报警。</p>
<p>八、如果不同意以上几条,请在出发前告知队长。</p>
<p>九、最重要的是:没有命,就什么都没了。</p>
"""
@staticmethod
def time_stamp(tp):
"""将前端传递的时间戳与当前时间戳对比并返回差值秒数"""
tamp = int(tp)
now = round(time())
sub_tamp = datetime.fromtimestamp(now) - datetime.fromtimestamp(tamp)
# total_seconds()包括天和秒数,获取两时间的总差;seconds():不包括天,只有秒数
return sub_tamp.total_seconds()
@staticmethod
def hex5(value):
"""加密后返回字符串"""
md_5 = hashlib.md5()
md_5.update(value.encode('utf-8'))
# 返回作为16进制数据字符串
md5_str = md_5.hexdigest()
return md5_str
def comparison(self, actions, tim, randstr, sign):
"""根据传递的参数计算MD5值,并与客户端提交的MD5值进行对比"""
value = actions+tim+randstr
hexs = self.hex5(value)
if hexs == sign:
return True
return False
def get(self, *args, **kwargs):
"""返回数据给视图"""
# 请求获取正文
params = self.request.arguments
actions = params.get('actions')[0].decode('utf-8')
tim = params.get('tim')[0].decode('utf-8')
randstr = params.get('randstr')[0].decode('utf-8')
sign = params.get('sign')[0].decode('utf-8')
# 取差值
seconds = self.time_stamp(tim)
if self.comparison(actions, tim, randstr, sign) and seconds < 5:
# 如果参数值都符合要求,就返回正常内容
self.write(self.content)
else:
# 否则返回异常
self.set_status(403)
def make_app():
return Application(
[(r'/', IndexHandler), (r'/fet/', FetHandler)],
template_path=os.path.join(os.getcwd(), 'templates'),
static_path=os.path.join(os.getcwd(), 'static'),
)
if __name__ == '__main__':
app = make_app()
app.listen(8000)
IOLoop.current().start()
点击‘点击查看详情’,测试成功!