引言
随着互联网的飞速发展, 爬虫技术不断演进, 为数据获取和信息处理提供了强大支持。然而, 滥用爬虫和恶意爬取数据的行为日益增多, 引发了反爬虫技术的兴起。在这场看似永无止境的 技术较量
中, 爬虫与反爬虫技术相互博弈、角力。本文将简单过下目前已知的几种反爬策略, 旨在扩展知识! 万一日后能够用上呢!!
一、图⽚伪装
1.1 原理
将价格、数量、手机号等一系列敏感信息, 通过图片的方式进行伪装, 然后图片和文字再混合一起进行展示, 这种方式既不影响用户的正常阅读, 又可以限制爬虫程序直接获取到这些敏感内容
1.2 破解方式
该反爬手段是直接用图片替换了原来的内容, 所以爬虫程序是无法直接获取内容的, 唯一的破解手段就是将图片下载下来, 然后使用 OCR(文字识别)
技术对图片内容进行一个识别
二、CSS 偏移
2.1 原理
在 HTML
中将要展示的内容打乱, 然后利用 CSS
将乱序的文字排版成人类能够正常阅读的顺序
例子: 下面我们来看 去哪儿网-机票列表 上的一个例子, 如下图所示页面正常展示了价格 810
, 但是如果我们查看 html
会发现, 价格对应的标签下是一组乱序的数字, 每个数字被标签包裹, 每个标签通过 CSS
进行了偏移
2.2 破解办法
需要找到偏移规律, 然后根据规律排列出正确的数据! 如上例子, 根据观察是通过 left
进行偏移的, 那么这里就可以提取出每个标签中 left
的值, 然后根据规律来排列出真实的数据!
三、自定义字体
3.1 扫盲: Unicode 和 字体文件
Unicode 又称为 统一码
, 它约定了每个已知的 字符
都应该对应着一个唯一的 Unicode
, 那么 字体
的作用其实就是约定了每个 Unicode
对应的字符应该被渲染成什么样的, 如下图是来自 Mac
「字体册」上的一个截图, 它定义了 Unicode
等于 4EEC
的字符应该是是长啥样的
3.2 原理
通过自定义一套字体文件, 可以是新增或者篡改字体, 来实现反爬, 爬虫程序通过复制或者简单的采集是无法采集到编码后的文字内容, 这里有两种方式:
- 新增: 通过新增字形来实现反爬, 调用自定义的
ttd
等字体文件来渲染网页中的文字, 而网页源码中的文字不再是文字, 而是相应的字体编码
具体案例可以看 猫眼电影 详情页中电影评分部分, 页面展示正常, 但如果查看元素会发现编码是错误的, 因为这里用的 Unicode
是自定义的, 我们系统是没有对应编码的字形
- 篡改字体: 通过修改原有字体中文字的
字形
来实现反爬, 这里我们来看下 谷雨解字 中的实现, 如下图, 通过修改字体文件中每个文字的字形来实现反爬, 其中文字今
将展示为八
, 文字晚
将展示为月
, 文字的
将展示为十
....
这里推荐下: 谷雨在线工具, 可以提供一段 原本
以及 阴书
生成解密字体文件
补充:
- 为了避免字体文件太大, 影响字体加载速度、页面渲染速度, 我们这里可以只针对必要的词进行反爬, 比如: 量词、否定词等等
- 同时为了加大爬虫难度, 可以随机生成多套(成百上千)字体库, 并将文字和
unicode
的映射存储起来, 然后在展示内容时随机抽取字体库, 将对应的文字替换成Unicode
编码
3.3 破解
只要知道原理, 就肯定有办法进行破解:
- 找到规律: 反爬内容对应的字体文件
- 获取字体: 将相关字体进行下载, 并保存为
xml
格式 - 创建映射关系: 解析
xml
, 并创建unicode
和文字的映射关系 - 转换: 遍历爬到的数据, 将反爬的内容进行还原
3.4 补充内容: 字体修改演示
做个补充, 本节将简单介绍下如何基于一个字体库, 进行一个简单的新增、修改等操作
-
首先我们需要先找一个基础的字体, 后面我们将在这份字体的基础之上进行修改, 我这里是网上随便找的一个 字体
-
使用 TTF to SVG Converter 将
TTF
字体文件, 转换为svg
- 进入网站, 点击
Slect File
上传下载好的基础字体
- 选择
Convert
将字体转换为svg
- 等待转换, 转换成功会有个弹窗, 点击下载即可
- 下面我们将使用 百度字体编辑器, 来对字体进行编辑
- 进入编辑器, 新建项目
- 导入上面转换后的
svg
- 修改已有文字字形: 选中要修改的文字, 点击编辑将打开字形编辑器, 这里直接进行编辑保存即可, 这里我将所有
0
字形修改为6
, 这样如果使用该字体文件, 那么源码中所有0
在页面上都会被展示为6
- 新增字形: 点击
+ 新字形
会基于现有unicode
追加一个unicode
并生成一个新的空白字形, 然后点击编辑来创建一个新的字形
- 下载: 点击如下按钮导出对应格式字体
- 使用: 引入字体, 并针对指定内容运用
css
@font-face {
font-family: fonteditor;
src: url("./fonteditor.ttf");
}
.text {
font-family: fonteditor;
}
四、SVG 图形映射
SVG
全称为 Scalable Vector Graphics
, 是一种基于 XML
并可以缩放的矢量图片文件格式, 对于 SVG
的使用可参考 iconfont 上的描述
4.1 原理
和上文提到的 图⽚伪装
和 自定义字体
其实本质上有点类似, 通过 SVG
来展示页面上一些敏感信息, 在不影响用户正常阅读同时, 让爬虫程序无法像读取文字那样获得 SVG
图形中的内容; 由于 SVG
中的图形代表的也是一个个文字, 所以在使用时必须在后端或前端将真实的文字与对应的 SVG
图形进行映射和替换
4.2 破解
SVG
可以被作为图片被使用, 也可作为字体文件进行使用, 这里需要找到源网站使用 SVG
的使用方式, 然后找到其规律进行破解, 当然实在不行直接使用 OCR(文字识别)
技术对内容进行一个识别
五、数据干扰
5.1 原理
随机在 html
中在要混淆的内容中插入干扰数据, 比如页面上我们可能要展示的内容为 101
, 但 html
中则在 101
中掺杂了干扰数据, 然后通过 CSS
将干扰的数据给隐藏掉, 阅读者不受干扰, 但是爬虫程序正常爬取的话将爬到错误的数据
如下图所示, 页面上展示的内容为 101
, 但是查看 html
会发现实际 innerText
应该是 19011
当然 CSS
隐藏元素的方式有很多, 为了增加反爬难度我们可以动态生成类名、并且随机使用一种 CSS
藏元素技巧
5.2 破解
只需要找到 CSS
隐藏元素的方式, 在抓取内容后判断 style
将干扰数据去除即可
六、User-Agent 浏览器识别
User-Agent
: 即用户代理, 简称 UA
, 它是一个特殊字符串头; 该字符串包含了用户发起网络请求对应软件的应用类型、操作系统、软件开发商以及版本号信息; 在网络请求当中, UA
是标明身份的一种标识, 服务器可以通过请求头参数中的 User-Agent
字段来判断请求方到底是浏览器、客户端程序还是其他的终端, 当然 User-Agent
的值为空也是允许的, 因为它不是必要参数
如下图, 当我们用浏览器访问 Web
应用的时候, 浏览器会把自己的 UA
信息加到 HTTP
请求的 User-Agent
头字段进行传输
6.1 原理
从上面的介绍中, UA
是作为用户身份标识, 当一个请求被发起, 意味着服务器可以清楚的知道该请求是否是从浏览器端发起、还是应用程序(比如 Python
)发起的, 那么自然服务端就能够通过这一特性, 通过鉴别 UA
来作为一个反爬手段
6.2 破解
User-Agent
请求头的值是可以被修改的, 当发起一个请求时可通过设置 headers
参数来篡改 User-Agent
, 然后将爬虫程序伪装成浏览器, 以此来骗过反爬虫程序
js
headers = {
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'sec-ch-ua': '"Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document',
'Referer': 'https://bj.ke.com/',
'Accept-Language': 'zh-CN,zh;q=0.9',
}
response = requests.get(url, headers=headers)
七、Cookie 验证
HTTP Cookie
是服务器发送到用户浏览器并保存在用户本地的一小块数据, 当请求响应头中存在 Set-Cookie
时浏览器会自动存储 Cookie
并在下次向同一服务器再发起请求时携带上该 Cookie
并发送到服务器上, 通常, 它用于告知服务端两个请求是否来自同一浏览器、同一用户(保持用户的登录状态)
7.1 原理
上文提到 Cookie
可以用于 Web
服务器的用户身份信息存储或状态保持, 但实际上我们还能够通过它实现简单的反爬措施, 首先需要手动设置一个固定格式的 Cookie
值, 然后每次浏览器发起请求都会自动把 Cookie
携带上, 服务端则会检验每个请求头中是否存在指定规则的 Cookie
, 如果存在则正常访问资源, 否则重定向到错误页面
Cookie
规则约定: 这里的 Cookie
一般由 JS
动态生成, 并且符合一个约定好的固定的规则, 这样 Cookie
值传递到后台之后, 服务端就可以直接验证该值即可实现反爬效果; 例如 Cookie
规则为 123abc123
, 前面 3
个随机数, 后面 3
个随机数, 中间三个随机小写字母, 那后台工程师就可以通过正则验证客户端传递的 Cookie
值, 是否符合规则, 不符合, 直接返回异常信息; 当然这种手段很容易被识别出来, 进一步还可以加入时间戳, 服务端拿到 Cookie
中的时间戳之后, 验证当前时间的差值, 如果超过了某个值(比如 10s
), 则认为该 Cookie
是伪造的
7.2 破解
找到 Cookie
约定的规则, 伪造一个 Cookie
即可
八、签名验证
签名验证是防止服务器被恶意调用, 避免数据被篡改的有效方式之一, 也是目前后端 API
最常用的防护方式之一, 同时通过 签名验证
也是可以实现有效的反爬措施
8.1 原理
签名验证反爬虫有多种实现方式,但是它的实现原理都是相同的, 都是由客户端生成唯一的 Token
- 这个值的生成可以类似上文
Cookie
一样有一个固定规则, 也可以是根据数据动态生成, 甚至可以是请求参数MD5
的一个结果 - 最后可通过请求参数、请求头等方式将签名发送给服务器, 然后服务器对签名进行一个校验
8.2 破解
大部分 签名验证反爬
的内容都是由 JS
生成的, 我们在遇到它时, 只需要去 JS
页面中寻找相应的 JS
文件, 从而发现它的加密方式, 进而来绕过它
九、Referer
HTTP
Referer
是请求头里面的一个参数, 当浏览器向 web
服务器发送请求的时候, 一般会带上 Referer
, 表明这个网页是从哪里跳过来的
9.1 原理
服务端通过通过检查 Referer
的正确性, 来实现简单的反爬
9.2 破解
破解方法也很简单, 在构造 header
的时候传入 Referer
参数即可
十、用户行为分析: 封 IP、验证码验证
10.1 原理
爬虫程序的访问速度和目的和正常用户是有很大区别的, 大多数爬虫具有无节制、大批量对访问目标进行爬取的行为, 而根据这个特性我们很容易做一些限制:
-
封
IP
(用户): 如果同一时间段内某一个或某几个IP
(用户) 访问量或者访问频率特别大, 且相邻请求时间间隔较为固定, 那么基本就可以判定此类行为为爬虫所为, 此时就可以在服务器上对异常IP
(用户) 进行封锁 -
图形验证码: 当然凡事都有例外, 万一某
IP
(用户) 就是访问快了一些, 这时还有一个措施就是, 在系统判断用户为非真实用户情况下, 可以弹出或者跳转到一个图形验证码页面, 只有当输入正确的验证码的情况下才能够继续访问 -
短信或邮箱验证: 同验证码类似, 只是这里改为了短信或者邮箱, 在系统判断用户为非真实用户情况下会给用户的手机或者邮件发送验证码, 并弹出或者跳转到一个填写验证码的页面, 只有当输入正确的验证码的情况下才能够继续访问
10.2 破解办法
- 使用
IP
(用户)池, 不断不定时切换IP
(用户) - 爬取频率控制: 访问时间间隔不要太短, 并且最好间隔时间不要固定而是随机的
- 针对图形验证码可以使用一些打码平台来破解
- 针对短信或邮箱验证, 手机号可以使用一些第三方平台的虚拟号, 并通过对应平台的
API
来获取短信验证码
十一、数据蜜罐(隐藏链接)
蜜罐最早是来自于网络攻防中, 主要指防守方故意设置一个或多个服务器漏洞, 让攻击方轻易地入侵进来, 而这些服务器一般安装了监控软件用来监控入侵者的一举一动。
11.1 原理
这里借用 蜜罐
的思路, 在页面中设置一些隐藏的链接或者图片资源, 然后通过 CSS
进行一个隐藏, 正常用户肯定是无法查看到该资源, 但是爬虫并不知道该资源是个陷阱依然按照正常逻辑进行访问, 服务端一旦发现有人访问了这些资源, 那么就判断该用户为爬虫程序, 就会将对应 IP
或者用户进行封禁
11.2 破解
由于爬虫工程师在分析网页时通常只需要找到目标数据的元素位置, 所以隐藏的特殊超链接并不容易被发现, 这其实利用的是爬虫工程师的粗心, 隐藏链接这种反爬虫手段很适合网站中的列表页, 所以在分析页面的时候只需要小心谨慎一点就行, 找到陷阱并绕过去就行
十二、WebDriver
Selenium
是一个自动化测试工具, 利用它可以 驱动浏览器
执行特定的操作, 比如点击、下拉等操作, 同时还可以获取浏览器当前呈现的页面源代码, 做到 所见即所得 。对于一些使用 JS
动态渲染的页面来说, 此种抓取方式非常有效!用大白话来讲就是使用 Selenium
可以控制我们的浏览器, 让浏览器打开指定的网页, 并进行一些列的操作!!!
WebDriver
是一个浏览器远程控制协议, Selenium
之所以能够控制浏览器, 就是通过该协议实现的! 同时如果我们通过 WebDriver
来控制浏览器, 那么在 window.navigator
上就会多了很多相关属性:
js
webdriver
__driver_evaluate
__webdriver_evaluate
__selenium_evaluate
__fxdriver_evaluate
__driver_unwrapped
__webdriver_unwrapped
__selenium_unwrapped
__fxdriver_unwrapped
_Selenium_IDE_Recorder
_selenium
calledSelenium
_WEBDRIVER_ELEM_CACHE
ChromeDriverw
driver-evaluate
webdriver-evaluate
selenium-evaluate
webdriverCommand
webdriver-evaluate-response
__webdriverFunc
__webdriver_script_fn
__$webdriverAsyncExecutor
__lastWatirAlert
__lastWatirConfirm
__lastWatirPrompt
12.1 原理
上文说到如果通过 Selenium
来控制浏览器操作页面, 那么在 window.navigator
上会多很多 WebDriver
协议相关属性, 我们就可以通过判断这些参数是否存在, 来识别出爬虫程序
js
<script>
if(window.navigator.webdriver === true){
document.write("<span>看到这段就代表你是爬虫</span>")
} else {
document.write("<span>真正的信息在这儿呢</span>")
}
</script>
12.2 破解
破解办法就是尽可能抹除 WebDriver
特征, 这里可以参考 extract-stealth-evasions, 这是一个 npm
工具包, 通过它我们可以生成一个 JS
文件, 在打开任何页面之前, 先运行这个 JS
文件, 就可以成功将这些特征隐藏!
十三、反 JS 逆向: 代码混淆、阻止事件、⽆限启动 debugger
很多反反爬手段都需要查看 JS
的源码, 找到反爬部分的逻辑, 所以为了防止他人查看 JS
源码常常会有以下几种措施:
JS
代码混淆: 通过在线工具, 或者Webpack
打包等工具, 将代码混淆成可读性低的代码, 看上去是难以阅读的代码, 但其实具备和之前代码一样的功能, 从而起到保护代码的作用- 阻止
F12
事件: 为避免他人通过F12
开启浏览器控制台查看源码, 可添加全局键盘事件, 如果用户按下F12
就阻止默认行为(打开控制台)
js
window.onkeydown = window.onkeyup = window.onkeypress = function (event) {
// 判断是否按下F12,F12键码为123
if (event.keyCode = 123) {
event.preventDefault() // 阻止默认事件行为
window.event.returnValue = false
}
}
- 阻止右键事件: 为避免他人通过鼠标右键, 开启浏览器控制台查看源码, 可添加
oncontextmenu
并阻止默认行为(无法右键打开菜单)
js
window.oncontextmenu = function() {
event.preventDefault() // 阻止默认事件行为
return false
}
- ⽆限启动
debugger
: 为避免他人通过控制台查看源码, 通过一些小手段, 迫使别人只要打开控制台就进入无限debugger
, 从而干扰他的行为
js
// 下面是混淆后的代码
const check = () => {
function doCheck(a) {
if (("" + a / a)["length"] !== 1 || a % 20 === 0) {
(function() {}["constructor"]("debugger")())
} else {
(function() {}["constructor"]("debugger")())
}
doCheck(++a)
}
try {
doCheck(0)
} catch (err) {}
};
setInterval(check, 1000);
check()
十四、接口加密
14.1 原理
为了防止爬虫直接调用服务端 API
来抓取数据, 我们可能还会对接口返回的数据进行一个加密, 严格点可能还需要对接口的参数进行加密, 这里一般采用的是对称加密(比如常见的 AES
)
- 前端在调用
API
时需要对请求参数进行一个加密 - 后端对参数进行解密, 然后获取数据, 并将数据进行加密后返回
- 前端拿到加密的数据, 进行解密
补充: 对于加密的秘钥的存储, 可能有以下几种方式:
- 前后端各存储一份(写死)
- 后端生成, 前端通过接口获取, 这里后端可以定时更新秘钥
- 前端随机生成, 通过
cookie
等方式带给后端
14.2 破解
找到加密方式、以及秘钥生成规律! 就能轻松破解
十五、总结
本文到此也就结束了, 在本文中主要就是简单介绍了下反爬常见的几种方案, 旨在做个扫盲, 实际上每种反爬方式如果要深入都能够整一篇长篇大论!!!
当然反爬本来就是不归路, 没有终点, 有反爬就有反反爬, 所有反爬的目的就是让爬虫采集的成本不断增加, 直到放弃, 那么反爬也就算那是成功了!!!