gzip炸弹—埋雷与拆弹
对于反爬虫—如何埋炸弹
前提:知道某个请求是爬虫发来的了,你不满足于单单屏蔽对方,而是想搞死对方。
原理
Requests 文档中的 Binary Response Content[1] 这一小节,看到这样一句话:
The gzip and deflate transfer-encodings are automatically decoded for you.
(Request)会自动为你把 gzip 和 deflate 转码后的数据进行解码
网站服务器可能会使用 gzip 压缩一些大资源,这些资源在网络上传输的时候,是压缩后的二进制格式。客户端收到返回以后,如果发现返回的 Headers 里面有一个字段叫做 Content-Encoding,其中的值包含 gzip,那么客户端就会先使用 gzip 对数据进行解压,解压完成以后再把它呈现到客户端上面。浏览器自动就会做这个事情,用户是感知不到这个事情发生的。而 requests、Scrapy 这种网络请求库或者爬虫框架,也会帮你做这个事情,因此你不需要手动对网站返回的数据解压缩。
文件之所以能压缩,是因为里面有大量重复的元素,这些元素可以通过一种更简单的方式来表示。压缩的算法有很多种,其中最常见的一种方式,我们用一个例子来解释。
假设有一个字符串,它长成下面这样:
1 | |
我们可以用5个字符来表示:192个1。这就相当于把192个字符压缩成了5个字符,压缩率高达97.4%。
如果我们可以把一个1GB的文件压缩成1MB,那么对服务器来说,仅仅是返回了1MB的二进制数据,不会造成任何影响。但是对客户端或者爬虫来说,它拿到这个1MB的数据以后,就会在内存中把它还原成1GB的内容。这样一瞬间爬虫占用的内存就增大了1GB。如果我们再进一步增大这个原始数据,那么很容易就可以把爬虫所在的服务器内存全部沾满,轻者服务器直接杀死爬虫进程,重则爬虫服务器直接死机。
制造炸弹
1 | |
生成的这个 boom.gz 文件只有 995KB:

使用 gzip -d boom.gz 命令解压后发现生成了一个 1GB 的 boom 文件:

把命令里面的count=1000改成一个更大的数字,就能得到更大的文件。
对于爬虫—如何避免踩炸弹
① 隐藏自己
把你的爬虫隐藏起来,因为gzip炸弹只能在发现了爬虫以后使用,否则就会影响到正常用户。只要你的爬虫让网站无法发现,那么自然就不会踩中炸弹。
…
② 识别出gzip炸弹
当你不读取resp.content、resp.text的时候,Requests是不会擅自给你解压缩的,如下图所示。因此你可以放心查看Headers:

如果发现网站返回的内容确实是 gzip 压缩后的内容,如何做到既不解压缩,又能获取到解压以后的大小?
如果你本地检查一个.gz文件,那么你可以使用命令gzip -l xxx.gz来查看它的头信息:

打印出来的数据中,第一个数字是压缩后的大小,第二个数字是解压以后的大小,第三个百分比是压缩率。这些信息是储存在压缩文件的头部信息中的,不用解压就能获取到。
那么当我使用Requests的时候,如何获得压缩后的二进制数据,防止它擅自解压缩?方法其实非常简单:
1 | |
运行效果如下图所示:

这个大小是压缩后的二进制数据的大小。
可以使用如下代码,在不解压的情况下,查询到解压缩后的文件大小:
1 | |
运行效果如下图所示:

总结
判断 resp.headers 中,是否有一个名为 content-encoding,值包含 gzip 或 deflate 的字段。
不包含?
- 不是gzip炸弹
包含?
- 不解压的情况下,查询到解压缩后的文件大小:
1 | |
文章来源: