服务器反爬的原因
1. 爬虫占总PV较高,浪费资源
2. 资源被批量抓走,丧失竞争力
3. 法律的灰色地带
服务器常反什么样的爬虫
1. 十分低级的应届毕业生
根本不管服务器的压力,很容易把站点搞挂
2. 十分低级的创业小公司
出于公司生存亡的考虑,不断爬取数据
3. 失控的爬虫
4. 成型的竞争对手
5. 抽风的搜索引擎
会导致服务器性能下降,请求量和网络攻击没什么区别
反爬虫领域中常见的一些概念
爬虫: 使用任何技术手段,批量获取网站信息的一种方式。关键也在于批量。
反爬虫: 使用任何技术手段,阻止别人批量获取自己网站信息的一种方式。关键也在于批量。
误伤: 在反爬虫的过程中,错误的将普通用户识别为爬虫。误伤率高的反爬策略,效果再好也不能用。
拦截: 成功地将阻止爬虫访问。这里会有拦截率的概念。通常来说,拦截率越高的反爬虫策略,误伤的可能性就越高。因此需要做一个权衡。
反爬的三个方向
1. 基于身份识别进行反爬
2. 基于爬虫行为进行反爬
3. 基于数据加密进行反爬
一、基于身份识别进行反爬
1. 通过headers字段进行反爬
headers中有很多的字段,这些字段都有可能会被对方服务器拿过来进行判断是否为爬虫
1.1 通过headers中的User-Agent字段来反爬
反爬原理: 爬虫默认情况下不会带上referer字段,服务器端通过判断请求发起的源头,一次判断请求是否合法。
解决方法: 请求之前添加User-Agent即可; 更好的方式是使用User-Agent池来解决(收集一堆User-Agent的方式,或者是随机生成User-Agent)。
1.2 通过referer字段或者是其他字段来反爬
反爬原理: 爬虫默认情况下不会带上referer字段,服务器端通过判断请求发起的源头,以此判断请求是否合法。
解决方法: 添加referer字段。
1.3 通过cookie来反爬
反爬原理: 通过检查cookies来查看发起的请求的用户是否具备响应的权限,以此来进行反爬。
解决方法: 进行模拟登录,成功获取cookies之后在进行数据爬取。
2. 通过请求参数来反爬
请求参数的获取方法有很多,向服务器发送请求,很多时候需要携带请求参数,通常服务器端酒可以通过
检查请求参数是否正确来判断是否为爬虫
2.1 通过html静态文件中获取请求数据(GitHub登录数据)
反爬原因: 通过增加获取请求参数的难度进行反爬。
解决方法: 仔细分析抓包得到的每一个包,搞清楚请求之间的联系。
2.2 通过发送请求获取请求数据
反爬原因: 通过增加获取请求参数的难度进行反爬。
解决方法: 仔细分析抓包得到的每一个包,搞清楚请求之间的联系,搞清楚请求参数的来源。
2.3 通过js生成请求参数
反爬原因: js生成了请求参数。
解决方法: 分析js,观察加密的实现过程,通过js2py获取js的执行结果,或者使用selenium来实现。
2.4 通过验证码来反爬
反爬原因: 对方服务器通过弹出验证码强制验证用户浏览行为。
解决方法: 打码平台或者是机器学习的方法识别验证码,其中打码平台廉价易用,更值得推荐。
二、基于爬虫行为的反爬
1. 基于请求频率或总请求数量
爬虫的行为与普通用户有着明显的区别,爬虫的请求频率与请求次数要远高于普通用户
1.1 通过请求的ip/账号单位时间内总请求数量进行反爬
反爬原理: 正常浏览器请求网站,速度不会太快,同一个ip/账号大量请求了对方服务器,有更大的可能性会被识别为爬虫。
解决方法: 对应的通过购买高质量的ip的方式能够解决问题/购买个多账号。
1.2 通过同一ip/账号请求之间的间隔进行反爬
反爬原理: 正常人操作浏览器浏览网站,请求之间的时间间隔是随机的,而爬虫前后两个请求之间时间间隔通常比较固定同时时间间隔较短,因此可以用来做反爬。
解决方法: 请求之间进行随机等待,模拟真实用户操作,在添加时间间隔后,为了能够高速获取数据,尽量使用代理池,如果是账号,则将账号请求之间设置随机休眠。
1.3 通过对请求ip/账号每天请求次数设置阈值进行反爬
反爬原理: 正常的浏览行为,其一天的请求次数是有限的,通常超过某一个值,服务器就会拒绝响应。
解决方法: 对应的通过购买高质量的ip的方法/多账号,同时设置请求间随机休眠。
2. 根据爬取行为进行反爬,通常在爬取步骤上做分析
2.1 通过js实现跳转来反爬
反爬原理: js实现页面跳转,无法在源码中获取下一页url。
解决方法: 多次抓包获取条状url,分析规律。
2.2 通过蜜罐(陷阱)获取爬虫ip(或者代理ip),进行反爬
反爬原理: 在爬虫获取链接进行请求的过程中,爬虫会根据正则,xpath,css等方式进行后续链接的提取,此时服务器端可以设置一个陷阱url,会被提取规则获取,但是正常用户无法获取,这样就能有效的区分爬虫和正常用户。
解决方法: 完成爬虫的编写之后,使用代理批量爬取测试/仔细分析响应内容结构,找出页面中存在的陷阱。
2.3 通过假数据进行反爬
反爬原理∶ 向返回的响应中添加假数据污染数据库,通常家属剧不会被正常用户看到。
解决方法: 长期运行,核对数据库中数据同实际页面中数据对应情况,如果存在问题/仔细分析响应内容。
2.4 阻塞任务队列
反爬原理: 通过生成大量垃圾url,从而阻塞任务队列,降低爬虫的实际工作效率。
解决方法: 观察运行过程中请求响应状态/仔细分析源码获取垃圾url生成规则,对URL进行过滤。
2.5 阻塞网络IO
反爬原理: 发送请求获取响应的过程实际上就是下载的过程,在任务队列中混入一个大文件的url,当爬虫在进行该请求时将会占用网络io,如果是有多线程则会占用线程。
解决方法: 观察爬虫运行状态/多线程对请求线程计时/发送请求钱。
2.6 运维平台综合审计
反爬原理: 通过运维平台进行综合管理,通常采用复合型反爬虫策略,多种手段同时使用。
解决方法: 仔细观察分析,长期运行测试目标网站,检查数据采集速度,多方面处理。
三、常见基于数据加密进行反爬
1. 对响应中含有的数据进行特殊化处理
通常的特殊化处理主要指的是css数据偏移/自定义字体/数据加密/数据图片/特殊编码格式等
1.1 通过自定义字体来反爬 下图来自猫眼电影PC端
反爬思路: 使用自由字体文件。
解决思路: 切换到手机版/解析字体文件进行翻译
1.2 通过css来反爬 下图来自去哪儿PC端
反爬思路: 源码数据不为真正数据,需要通过css位移才能产生真正数据。
解决思路: 计算css的偏移。
1.3 通过js4动态生成数据进行反爬
反爬思路: 通过js动态生成。
解决思路: 解析关键js,获得数据生成流程,模拟生成数据。
1.4 通过数据图片化反爬
58同城短租(https://baise.58.com/duanzu/38018718834984x.shtml)
解决思路: 通过使用图片解析引擎从图片中解析数据。
1.5 通过编码格式进行反爬
反爬思路: 不适用默认编码格式,在获取响应之后通常爬虫使用utf-8格式进行解码,此时解码结果将会是乱码或者报错。
解决思路: 根据源码进行多格式解码,或者真正的解码格式。
验证码
1. 图片验证码
1.1 什么是图片验证码
验证码(CAPTCHA)是"Completely Automated Public Turing test to tell Computers and HumansApart”(全自动区苏计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。
1.2 验证码的作用
防止恶意破解密码、刷票、论坛灌水、刷页。有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登录尝试,实际上使用验证码是现在很多网站通行的方式(比如招商银行的网上个人银行,百度社区),我们利用比较简易的方式实现了这个功能。虽然登录麻烦一点,但是对网友的密码安全来说这个功能还是很有必要,也很重要。
1.3 图片验证码在爬虫中的使用场景
注册
登录
频率发送请求时,服务器弹出验证码进行验证
1.4 图片验证码的处理方案
手动输入(input)这种方法仅限于登录一次就可持续使用的情况。
图像识别引擎解析使用光学识别引擎处理图片中的数据,目前常用于图片数据提取,较少用于验证码处理。
打码平台,爬虫常用的验证码解决方案。
2. 图片识别引擎
OCR(Optical Character Recognition)是指使用扫描仪或数码相机对文本资料进行扫描成图像文件,然后对图像文件进行分析处理,自动识别获取文字店息及版面信息的软件。
2.1 什么是tesseract
Tesseract,一款由HP实验室开发由Google维护的开源OCR引擎,特点是开源,免费,支持多语言,多平台。
项目地址: https://github.com/tesseract-ocr/tesseract
2.2 图片识别引擎环境的安装
(1). 引擎的安装
Mac环境下直接执行命令
brew install --with-training-tools tesseract
Windows环境下的安装,可以通过exe安装包安装,下载地址可以从GitHub项目中的wiki找到。安装完成后记得将Tesseract执行文件的目录加入到PATH中,方便后续的使用。
https://digi.bib.uni-mannheim.de/tesseract/
Linux环境下的安装
sudo apt-get install tesseract-ocr
(2). Python库的安装
# PIL用于打开图片文件
pip/pip3 install pillow
# pytesseract模块用于从图片中解析数据
pip/pip3 install pytesseract
2.3 图片识别引擎的使用
通过pytesseract模块的image_to_string方法就能将打开的图片文件中的数据提取成字符串数据,具体方法如下:
from PIL import Image
import pytesseract
im = Imageopen()
result = pytesseract.image_to_string(im)
print(result)
2.4 图片识别引擎的使用扩展
tesseract简单使用与训练
其他ocr平台
微软Azure图像识别: https://azure.microsoft.com/zh-cn/services/cognitive-services/computer-vision/quickstarts/java
有道智云文字识别: https://aidemo.youdao.com/ocrdemo
阿里云图文识别: https://www.aliyun.com/product/cdi/
腾讯OCR文字识别: https://cloud.tencent.com/product/ocr
百度AI文字识别: https://ai.baidu.com/ai-doc/OCR/zk3h7xz52
3. 打码平台
3.1 为什么需要了解打码平台的使用
现在很多网站都会使用验证码来进行反爬,所以为了能够更好的获取数据,需要了解如何使用打码平台爬虫中的验证码。
3.2 常见的打码平台
(1). 云打码: http://www.yundama.com/
能够解决通用的验证码识别
(2). 极限验证码智能识别辅助: http://jiyandoc.c2567.com/
能够解决复杂验证码的识别
(3). 查超级鹰打码平台: http://www.chaojiying.com/
可以解决一切验证码的识别,但是收费(价格非常低)
3.3 云打码的使用
下面以云打码为例,了解打码平台如何使用
(1). 云打码官方api接口
下面代码是云打码平台提供,做了个简单修改,实现了两个方法:
1. indetify: 传入图片的响应二进制数即可
2. indetify_by_filepath: 传入图片的路径即可识别
其中需要自己配置的地方是:
username = 'whoarewe' # 用户名
password = '******' # 密码
appid = 4283 # appid
aapkey = '02074c64f0d0bb9efb2df455537b01c3' # appkey
codetype = 1004 # 验证码类型
4. 常见的验证码的种类
4.1 url地址不变,验证码不变
这是验证码里面非常简单的一种类型,对应的只需要获取验证码的地址,然后请求,通过打码平台识别即可。
4.2 url地址不变,验证码变化
这种验证码的类型是更加常见的一种类型,对于这称殓证码,大家需要思考:
在登录的过程中,假设我输入的验证码是对的,对方服务器是如何判断当前我输入的验证码是显示在我屏幕上的验证码,而不是其他的验证码呢?
在获取网页的时候,请求验证码,以及提交验证码的时候,对方服务器肯定通过了某种手段验证我之前获取的验证码和最后提交的验证码是同一个验证码,那这个手段是什么手段呢?
很明显,就是通过cookie来实现的,所以对应的,在请求页面,请求验证码,提交验证码的到时候需要保证cookie的一致性,对此可以使用requests.session来解决。
小结:
1. 了解验证码的相关知识
2. 掌握图片识别引擎的使广
3. 了解常见的打码平台
4. 掌握通过打码平台处理验证码的方法
chrome浏览器的使用
1. 新建隐身窗口
浏览器中直接打开网站,会自动带上之前网站时保存的cookie,但是在爬虫中首次获取页面是没有携带cookie的,这种情况如何解决呢?
使用隐身窗口,首次打开网站,不会带上cookie,能够观察页面的获取情况,包括对方服务器如何设置cookie在本地。
2. chrome中network的更多功能
2.1 preserve log
默认情况下,页面发生跳转之后,之前的请求url地址等信息都会消失,勾选preserve log后之前的请求都会被保留。
2.2 filter过滤
在url地址很多的时候,可以在filter中输入部分url地址,对所有的url地址起到一定的过滤效果,具体位置在上面的第二幅图中的2的位置。
2.3 观察特定种类的请求
在上面第二幅图中的3的位置,有很多选项,默认是选择约all ,即会观察到所有种类的请求。
很多时候处于自己的目的可以选择all右边的其他选项,比如常见的选项:
XHR: 大部分情况表示ajax请求
JS: js请求
CSS: css请求
但是很多时候我们并不能保证我们需要的请求是什么类型,特别是我们不清楚一个请求是否为ajax请求的时候,直接选择all ,从前往后观察即可,其中js,css,图片等不去观察即可。
不要被浏览器中的一堆请求吓到了,这些请求中除了js,css,图片的请求外,其他的请求并没有多少个。
3. 寻找登录接口 http://www.renren.com(该网页已禁止使用)
3.1 寻找action对的url地址
可以发现,这个地址就是登录的form表单中action对应的url地址,回顾前端的知识点,可以发现就是进行表单提交的地址,对应的,提交的数据,仅仅需要: 用户名的input标签中,name的值作为键,用户名作为值,密码的input标签中,name的值作为键,密码作为值即可。
思考: 如果action对应的没有url地址的时候可以怎么做呢?
3.2 通过抓包寻找登录的url地址
通过抓包可以发现,在这个url地址和请求体中均有参数,比如uniqueTimestamp和rkey以及加密之后的password。
这个时候我们可以观察手机版的登录接口,是否也是一样的。
可以发现在手机版中,依然有参数,但是参数的个数少了一些,这个时候,我们可以使用手机版作为参考,下一节来学习如何分析js。
小结:
1. 使用隐身窗口的主要目的是为了避免首次打开网站携带cookie的问题。
2. chrome的network中,perserve log选项能够在页面发生跳转之后仍然能观察之前的请求。
3. 确定登录的地址有两种方法。
(1). 寻找from表单action的url地址
(2). 通过抓包获取
js加密解析
1. 定位js文件
1. 通过initiator定位到js文件
2. 通过search搜索关键字定位到js文件
3. 通过元素绑定的事件监听函数找到js文件
注: 三种方法不保证每一种都能找到js文件,三个方法都进行定位。
2. 分析js代码,掌握加密步骤。
1. 确定js的位置
1.1 观察按钮的绑定js事件
通过点击按钮,然后点击Event Listener,部分网站可以找到绑定的事件,对应的,只需要点击即可跳转到js的位置。
1.2 通过search all file(search)来搜索
部分网站的按钮可能并没有绑定js事件监听,那么这个时候可以通过搜索请求中的关键字来找到js的位置,比如livecell。
2. 观察js的执行过程
找到js的位置之后,我们可以来通过观察js的位置,找到js具体在如何执行,后续我们可以通过python程序来模拟js的执行,或者是使用类似js2py直接把js代码转化为python程序去执行。
观察js的执行过程最简单的方式是添加断电
添加断电的方式: 在左边行号,点击即可添加,对应的右边BreakPoints中会出现现有的所有断点。
添加断点之后继续点击登录,每次程序在断点位置都会停止,通过如果该行有变量产生,都会把变量的结果展示在Scoope中。
在上图的右上角有1,2,3三个功能,分别表示:
(1). 继续执行到下一个断点
(2). 进入调用的函数中
(3). 从调用的函数中跳出来
3. 模拟加密步骤,使用Python的方法重现。
1. 通过第三方js加载模块直接加载js运行
js2py的使用
1.1 js2py的介绍
js2py是一个js的翻译工具,也是一个通过纯python实现的js的解释器,GitHub上源码与示例
1.2 js的执行思路
js的执行方式大致分为两种:
1. 在了解了js内容和执行顺序之后,通过python来完成js的执行过程,得到结果。
2. 在了解了js内容和执行顺序之后,使用类型js2py的模块来执js代码,得到结果。
但是在使用python程序实现js的执行时候,需要观察的js额每一个步骤,非常麻烦,所以更多的时候我们会选择使用类型js2py的模块去执行js,接下来我们来使用js2py实现人人网登录参数的获取。
1.3 具体的实现
定位进行登录js代码从
2. 用python重现
定位进行登录js代码
formSubmit: function() {
var e, t = {};
$(".login").addEventListener("click", function() {
t.phoneNum = $(".phonenum").value,
t.password = $("password").value,
e = loginValidate(t),
t.c1 = c1 || 0,
e.flag ? ajaxFunc("get", "http://activity.renren.com/livecell/rKey", "", function(e) {
var n = JSON.parse(e).data;
if (0 == n.code) {
t.password = t.password.split("").reverse().join(""),
setMaxDigits(130);
var o = new RSAKeyPair(n.e,"",n.n)
, r = encryptedString(o, t.password);
t.password = r,
t.rKey = n.rKey
} else
toast("公钥获取失败"),
t.rKey = "";
ajaxFunc("post", "http://activity.renren.com/livecell/ajax/clog", t, function(e) {
var e = JSON.parse(e).logInfo;
0 == e.code ? location.href = localStorage.getItem("url") || "" : toast(e.msg)
})
}) : toast(e.msg)
})
}