万字长文教你对抗邪恶的爬虫

一个初级爬虫和正常的请求最大的区别就是请求头的中User-Agent,所谓User-Agent就是用来标识客户端应用程序的,不同的客户端(甚至同一客户端不同版本)之间都会有所区别。
首先看一下Chrome浏览器的User-Agent:

Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36

然后看一下IE浏览器的User-Agent:

Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko

最后看一下一个常用的Python爬虫库Requests的User
-Agent:

python-requests/2.20.1

区别很明显了,针对爬虫的反制措施也很容易:如果 User-Agent
检查没通过,直接返回 403
Forbidden。

下面是一段通过User-Agent拦截请求的Nginx配置示例:

server {

        listen 80;

        server_name 192.168.50.67 www.example.com;

        

        charset utf-8;

        access_log /var/log/nginx/access.log;

        error_log /var/log/nginx/error.log;

        # 通过正则匹配,如果发现User-Agent带有python或者postman直接返回403

        if ($http_user_agent ~* "python|postman") {

                return 403;

        }


location / { proxy_pass http://127.0.0.1:5000; } }

另外除了User-Agent之外,Referer和Cookies也是常用的区分爬虫和正常请求的请求头。
通过请求头来拦截爬虫的方式是所有方式中最简单的。由于近两年关于Python爬虫的文章和视频充斥着整个互联网,可以毫不客气的说:通过拦截User-Agent带有Python标识的请求可以拦截百分之八十的爬虫。
简单高效,几乎不会误伤到真实用户。不过,同时也要意识到有经验的爬虫作者会通过伪装User-Agent的方式来绕过拦截。

实用指数:五星

误伤指数:半星
绕过难度:一星半
(二)

基于IP访问频率的限制

初级爬虫的另一个特点就是野蛮,就像恶龙看到金子一样,丝毫不会掩饰自己的贪婪。因为爬虫的高效,相较于正常请求,如果遇到一个毫无自控力的爬虫对于服务器的压力是可想而知的。所以服务端限制单个IP的访问频率就显得非常有必要。
Nginx本身支持针对客户端访问限制,下面是Nginx的配置示例:

 http {

    # 因为http持久化连接的存在,单个连接往往可以发送多次请求 

    # limit_conn_zone 是限制单个ip的并发连接数。对应下面的limit_conn

    limit_conn_zone $binary_remote_addr zone=addr:10m;

    # limit_req_zone 是限制单个ip的并发请求数。对应下面的limit_req

    limit_req_zone $binary_remote_addr zone=one:10m rate=100r/s;

    server {

        listen       80;

        server_name  192.168.50.67 127.0.0.1;

        location / {

            proxy_pass  http://127.0.0.1:5000;

            limit_conn addr 100;

            limit_req zone=one  burst=150;

        }

    }

}

那么,是不是服务端做了访问限制就可以高枕无忧了呢?答案是否定的。当爬虫作者发现频率限制之后,往往会在很短的时间内就能够分析出服务器配置的 访问
频率的大概范围,如果能够满足自身需要,可以通过限制自己的访问频率来避免触发服务器的警戒值;即使发现服务器配置的访问频率太严格,不能满足自己的需要,也可以通过IP代理池来不断的变换IP地址。不过好消息是,相比基于请求头的限制,基于IP访问频率的限制方式很显著的提高了爬虫的成本,“痛则不通”,它可以帮助我们筛选掉那些对于成本敏感的爬虫,特别是每年三月份为了毕业论文行动起来的学生。
另外,基于IP访问频率的限制有一个明显的缺点,那就是容易误伤真实用户。要知道现实中很可能一栋楼都在用一个IP地址。

实用指数:三星半

误伤指数:三星半
绕过难度:三星
(二)

基于验证码的限制

前面两种方式主要是从行为特征的角度来试图识别某个请求是 正常请求
还是 爬虫
。但是如果爬虫伪装的足够好,会发现无论是基于请求头还是基于访问频率,都已经不能够用来判断一个请求是不是爬虫了。

”守则不足,攻则有余“,这时候往往需要转变被动防守的思维模式,主动出击。既然基于行为特征已经走进了死胡同,为什么不能让用户自己表明不是爬虫呢?验证码就是在这种背景下应运而生的,最常见的验证码形式是图形验证码和短信验证码


图形验证码
要求用户主动输入验证码,验证通过之后才可以继续下一步操作,这种方式可以很有效的帮助后台识别真实用户和爬虫。不过”道高一尺,魔高一丈“,爬虫阵营也发展出了对抗验证码的技术。以图形验证码为例,普通的图形验证码可以很容易的被现有的OCR技术识别。OCR技术的发展进而又导致了验证码中出现了噪点、干扰线、字母粘连等提高识别难度的手段。攻防对抗一旦形成,双方往往会不计代价的投入战斗。以谷歌的图形验证码为例,真人识别的成功率已经不足百分之五十,不禁让人思考这种牺牲用户体验的防御方式是不是真的值得。
虽然图形验证码走进了死胡同,但是近年来出现了很多种验证码的变种,比如滑块验证、拼图验证以及文字点选认证等形式,尽量的把对用户体验的影响降到最低。


某云厂商提供的验证方案

实用指数:三星

误伤指数:四星
绕过难度:四星

2

高级篇

既分高下,也决生死


在初级篇里面,我们介绍了三种对抗方式。可以毫不客气的说,一套组合拳打出去,百分之九十五的爬虫都会倒地不起。

但是也要意识到剩下的百分之五才是真正的对手,能够突破验证码限制的爬虫,往往是对服务器数据有着强烈兴趣,同时有着相对较高的技术水平,对于成本也有着 相对
更高的预算。此时的技术对抗的主要特征是,数据的生产方作为防守方先出招,爬虫方作为进攻方见招拆招。由于此阶段少不了加密技术的身影,对于防守方来说往往需要前后端开发人员协同作战。另外,不同于验证码的简单粗暴,看似紧张的高手过招,对于正常用户而言却是无感知的。
口说无凭,也是为了能更好的说明问题,老张请教了一个擅长爬虫的朋友——公输,分享他的两个案例跟大家详细说说。
(一)
某团外卖API携带的token
2018左右,当时还有市场上的几家外卖平台活动力度还比较大,作为肥宅的公输就想着利用爬虫找到自己家附近最优惠的商家。
鸡贼的公输选择了外卖平台的手机端H5平台。我们前面提到的三种限制方式,公输都遇到了,并且很轻易的就绕过了,当时老张还夸他就是那百分之五的高手,直到他遇到了某团外卖的token。

某团外卖展示的商家信息都是通过GET请求从服务器拉取的,每次请求的时候URL里面都会有一个accessToken参数,服务器会校验这个token参数,如果校验失败服务器会返回一个错误代码。简单的尝试之后,公输发现了事情并没有那么简单,token的生成规则关乎外卖爬虫的成败。

再见到他已经是一个月之后了

再见公输的第一面,激增的白发并不能掩盖他眼神里的故事。知道公输大功已成的老张还没来得及开口,公输却首先抛出了一个问题

公输:你听说过JS混淆吗?
老张:听过。好好的一段代码进去,混淆的连妈都不认识,但是调用的时候还跟没事人一样,用过的都说好。


一段混淆前后的JS代码对比
公输点头不语,突然的沉默反而激发了老张的好奇心。
老张:所以,隐藏在混淆之后的token到底是怎么是生成的?

听到老张问

了关键点,满头白发的公
微微一惊。

公输:罢了,跟你说了吧。首先,把所有的GET参数按照key排序组合成一个字符串,然后用zlib压缩,最后用base64转码,就是最后的token了。

老张:就这么简单?
公输:就这么简单!

老张:花了一个月?
公输:花了一个月!
(二)
某水果iTunes的登录流程
相信上一个例子已经很说明说明问题了:但凡正常用户可以的做到的,爬虫肯定也可以做到,只不过是成本或高或低。

可能有些同学不信服,觉得公输能够破解外卖平台的token是因为外卖平台提供了可以在浏览器使用的服务方式,而浏览器是没有秘密的,就算混淆后的JS也是可以通过开发者工具查看的,如果是客户端提供服务,经过编译的加密代码肯定没法破解。
老张:有人觉得客户端是铁板一块,爬虫遇到客户端就会死翘翘。
公输:呵呵,他肯定没听过IDA,对于逆向工程的力量一无所知。


IDA价格在几千美刀左右,你看知名黑客炫富都这么有内涵
老张:Talk is cheap, 你手头有没有案例。
公输:还真有,你知道iTunes吗?iTunes登录复杂吧,不一样搞它?

老张:知道,早期云计算能力有限,几乎每个水果手机用户都需要在电脑上安装iTunes用来同步
数据。我听说水果家的技术安全水平可是高的很。

公输:首先,iTunes会根据主机的硬件信息生成一个MD5值,然后请求两次服务器交换加密信息,最后才发送真正的登录请求。

老张:等等,说的跟真的一样,我怎么知道你有没有骗我?
公输:Show you the code,那个DLL文件就是一切的关键。

3

终级篇

他强由他强,清风拂山岗;他横由他横,明月照大江

透过高级篇里面的两个案例,甚至可以看到矛盾论的身影:矛盾的同一性和斗争性共同推动了事物的发展。具体到本篇文章就是:爬虫和反爬虫的技术对抗共同作用,推高了双方的技术成本。
可能到这里有些同学会有悲观情绪,觉得强如谷歌、微软这样的互联网巨头,也没法完全封杀爬虫。听老张一句劝,其实大可不必这么悲观,其实在爬虫与反爬虫的技术对抗中,主动权一直掌握在反爬虫一方的手中。前面验证码那一章提到了化被动为主动,现在老张再帮大家解放思想,介绍一下应对爬虫的终极手段——投毒。

前面我们已经介绍了多种的手段来识别一个请求是真实请求还是爬虫,在意识到是爬虫的时候,我们又是怎么做的呢?一般来说是返回403Forbidden,禁止爬虫的访问。高手过招,这种情况可能会适得其反,激发了爬虫的战斗欲望,不计成本的来绕过我们的反爬虫措施。但是如果我们不是返回 403
Forbidden

而是返回假数据呢?害怕太明显会被发现的话,再过分一点,如果一千次响应里面我们参杂了四百二十五条假数据呢?因为假数据的存在,爬虫获取的数据就是不可信数据,基于不可信数据甚至能推导出完全错误的结论,这样就从一个刁钻的角度保护了我方数据安全。这种明明已经识别爬虫,却故意返回假数据的方式就是投毒。
记得老张曾经跟公输提起到“投毒”,老张永远忘不了当时公输听完这两个字的时候虎躯一震,仿佛PTSD患者,脸上的表情像极了在酒馆遭受调侃的孔乙己。

一个反爬虫码农的快乐就是这么朴实无华,且枯燥!