最近一直在用django写一个个人音乐在线播放平台。 其中在网页数据保护方面,我采取了很多种的反爬虫措施,所以在本篇文章中,我从源码和实际操作上给大家分析下我所使用的反爬虫及其对应的破解技巧。
首先我们声明的是,爬虫和反爬虫没有高低之分,虽然总有一种方法能突破你的安全保护。 爬虫就像是一个钉子,反爬则是一扇铁窗。钉子坚持不懈,总能搞破窗。但是窗户是不能只针对于一点全力打造的。从此,修修补补,一般双方不下班不休。 下面我把反爬和反反爬分开来写。这样爱好不同的人可以各取所需。
反爬虫:
1、我在django-views中设置了登录身份验证,设置了装饰器,通过META.get获取请求头,限制请求头和访问间隔。
代码语言:javascript复制lxcsdn = ["https://blog.csdn.net/weixin_43582101/column/info/33034",
"https://blog.csdn.net/weixin_43582101/article/details/86563910",
"https://blog.csdn.net/weixin_43582101/article/details/86567367",
]
'''设置请求头和访问间隔'''
ips = [None] #存储客户端最后一次访问的ip地址 空列表也是列表,None类型只有None一个值
## ips=[None] 为了防止 list index out of range。
last = 0 #存储客服端最后一次访问时间
def isCraw(func):
def wrapper(*args,**kwargs):
global ips,last #声明全局变量
#request.META 是一个字典,包含了所有本次HTTP请求的Header信息
agent = args[0].META.get('HTTP_USER_AGENT') #获取请求头部信息
if 'Mozilla' not in agent and 'Safari' not in agent and 'Chrome'not in agent:
return HttpResponse(random.choice(lxcsdn))
else:
ip = args[0].META.get('REMOTE_ADDR') #客户端IP地址
# 什么是remote_addr:
# remote_addr 是服务端根据请求TCP包的ip指定的。假设从client到server中间没有任何代理,
# 那么web服务器(Nginx,Apache等)就会把client的IP设为IPremote_addr;
# 如果存在代理转发HTTP请求,web服务器会把最后一次代理服务器的IP设置为remote_addr
now = time.time()
if ip==ips[0] and now-last<2: #为了防止误伤
return HttpResponse("Are you curious about what happened? If you read this sentence carefully, you will know what happened. so the page is not found , But is you didn't find it ! " )
last = now
ips.pop()
ips.append(ip)
return func(*args,**kwargs)
return wrapper
!记得上面是一个装饰器。 mete.get可以获取请求头部的信息。如果没有请求头User-Agent的话,就只能请求到我的csdn博客链接。在最下面记录了上一次到这一次访问的ip时间,如果频率小于2秒就会跳转到错误页面。
2、设置了cookie和登录成功后的session,并通过url编码方式隐藏cookie cookie和session是网站的必须品,但是有的爬虫会通过cookie直接省去登录来访问你的主页从而提取数据。 因为url编码方式不支持中文,所以我们可以设置为中文及外文的cookie。 大致如下:
代码语言:javascript复制 response.set_cookie(getPassword(urlquote("你好")),getPassword(urlquote("公正法治民主")))
response.set_cookie((urlquote("아니카시유")),(urlquote("아니카시유아니카시유")))
在控制台上显示的内容则是:
这可以起到一定程度的反爬虫作用。当然你也可以使用已经加密过的数据,来加大爬虫破解的难度。
3、设置了自动刷新login页面,30秒计时。 为了防止selenuim和无界面浏览器的侵袭,只能尽量的牺牲一点用户体验,我们把浏览器刷新时间控制在30秒左右,运行缓慢的无界面浏览器就会很难受了。 这里我用JS来实现的。
代码语言:javascript复制 function myrefresh() {
window.location.reload();
}
setTimeout('myrefresh()', 30000);
4、上面设置了30秒的时间是不够的,所以我在登录界面设置了验证码,这样也能隔绝一大部分的爬虫初学者。
首先我写了一个鼠标移入显示图片的方法。因为要考虑到用户体验,又要考虑到selenuim的登录。所以我设置的标签为,鼠标移入外面的div,显示里面的图片,所以selenuim去获取里面图片的属性时,是获取不到的。
点击刷新验证码:为什么要写这个呢,对方如果通过点击触发图片的话,可能会在点击很触发的图片已经跟开始的不一样了,这个我也没试过,应该是这样。
代码语言:javascript复制 function refresh(ths)
{ths.src = ths.src '?'}
这个写起来特别简单,只需要在原click方法上给url 上一个?就可以重复请求。(如果两次请求url相同是不能刷新图片的)
下面是验证码生成的代码: (本篇代码为了方便讲解,我都写在视图中,有不懂请留言)
代码语言:javascript复制def auth_code(request):
size = (143,40)
width,height = size
font_list = list("abcdefghklmpqrstuvwxyz*#23456789")
c_chars = " ".join(random.sample(font_list,5))
request.session['auth'] = c_chars.replace(' ','')
img = Image.new("RGB",size,(30,31,42))
draw = ImageDraw.Draw(img)
# for i in range(random.randint(1,7)):
# draw.line(
# [
# (random.randint(0, 150), random.randint(0, 150)),
# (random.randint(0, 150), random.randint(0, 150))
# ],
# fill=(0, 0, 0)
# )
# for j in range(1000):
# draw.point(
# ( random.randint(0, 150),
# random.randint(0, 150)
# ),
# fill = (0, 0, 0)
# )
font = ImageFont.truetype("simsun.ttc", 23)
draw.text((5,4),c_chars,font=font,fill="white")
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params)
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
img.save(buf,'png')
return HttpResponse(buf.getvalue(),)
5、因为只是这样我感觉力度还是不够,有耐心的selenuim还是可以很快破解的。所以我从属性上来限制一下PhantomJS和selenuim。 这个限制是用js来写的。 …我刚突然发现我把这页的JS混淆压缩加密了,不过可以拿去直接用。
代码语言:javascript复制 window.onload=function(){
if (navigator.webdriver) {
eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c 29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\w '};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\b' e(c) '\b','g'),k[c]);return p;}('0 1='';',2,2,'var|a'.split('|'),0,{}));
var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c 29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\w '};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\b' e(c) '\b','g'),k[c]);return p;}('0 1='';0 1='';0 1='';0 1='';',2,2,'var|a'.split('|'),0,{}));
window.location.href="https://www.hexu0614.com/blog/blogs/12/"}
else if(/HeadlessChrome/.test(window.navigator.userAgent)) {
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
window.location.href="https://www.hexu0614.com/blog/blogs/6/";}
else if(navigator.plugins.length === 0 || !navigator.plugins.length) {
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
window.location.href="https://www.hexu0614.com/blog/blogs/33/";}
else if(navigator.languages === "") {
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
var a = '';var a = '';var a = '';var a = '';var a = '';var a = '';
window.location.href="https://www.hexu0614.com/blog/blogs/5/";}
// {# 获取图片判断 #}
var body = document.getElementsByTagName("body")[0];
var image = document.createElement("img");
image.src = "/static/tboys/img/login_ico.png";
image.setAttribute("id", "auth_img");
body.appendChild(image);
image.onerror = function(){
if(image.width === 0 && image.height === 0) {
window.location.href="https://www.hexu0614.com";
}}
};
首先上文中的方框是selenuim不能是别的字符集,会影响他的读取操作,有限制作用,如果添加的够多的话,可能会把他搞崩溃。 第一个if的作用是,如果访问的驱动是webdriver的话,就把页面跳转到别的地方。后面的两个作用是:通过判断访问来源的长度和语言来判断是不是无界面浏览器。最后面那个是通过对图片的识别方式来判断是不是无界面浏览器。 因为无界面浏览器在访问图片的时候,他的访问得到的图片宽高都是0;并且无界面浏览器的语言默认为空,长度默认为0.
6、设置了form表单post请求,通过display:none hidden进行加密隐藏 这个是常见的form表单。我通过大量的display:none 和标签的隐藏,来干扰爬虫对页面的判断和控制。从而去增加爬虫的难度。 并且可以给爬虫布置下一个蜜罐,让他一步一步进入错误的路径,并且毫不知情…
7、将主页音乐链接数据保存在.js中,JS文件经过混淆压缩加密。 假设对方破解了验证,通过登录到了主页中。也是很难发现他所需要的数据的。页面信息中并没有url信息。
我的url如下:
由于我的数据没有更新完成,这里并没有使用JS加密。 当你想加密JS可以去下图所示
或者自己找一个靠谱的,因为有的时候加密完,会把功能也给加密没了。 大家要一边尝试一边加密。
8、设置iframe。iframe 元素会创建包含另外一个文档的内联框架(即行内框架) 效果是这样的。
页面:
控制台显示如下:
你可以通过多个iframe来干扰爬虫的视角,隐藏自己的url。
9、比较难受人的一种css方法,通过字符集映射来改变页面信息和源码信息的不同。 利用前端字体文件(.ttf)混淆文本来阻止爬虫爬取网站数据.这个我不详细介绍了,举几个列子,大家可以网上搜索下具体操作。
- 1 FONT-FACE拼凑式
- 2 BACKGROUND拼凑式
- 3 字符穿插式
- 4 伪元素隐藏式
- 5 元素定位覆盖式
- 6 IFRAME异步加载式
- 7 字符分割式
- 8 字符集替换式
看起来是不是很刺激。 大家可以用fontcreator工具来构建自己的字体,然后在html页面导入自定义字体。
还有点什么反爬方式,我忘记了。。先不写了。 下面我们来逐条破解上面的反爬措施。
反反爬虫:
1、根据表单formdata数据,去提交对应数据。 2、仔细仔细再仔细 3、通过urldncode解密 4、通过图像识别,打码平台,或者软件工具来进行验证码处理 5、JS数据清洗,数据解密。 6、根据网站设置的限制方式,改变我们请求频率和请求内容。 7、用fontcreator来找出字符集加密规律。 8、使用无头浏览器,就去逐情改变他的属性 算了 不写了。反反爬的攻略太多了。大致上了解一下网上到处都是。 等到你越到的时候再查就行了。