Python网络爬虫技术与实战
上QQ阅读APP看书,第一时间看更新

2.6.1 反爬虫

反爬虫暂时是一个较新的领域,本书对反爬虫的定义为:使用任何技术手段,阻止别人批量获取自己网站信息的一种方式。反爬虫技术一方面是不让真实的数据被大规模爬取,另一方面也是为爬虫爬到的数据增加处理上的负担。

下面介绍反爬虫的四个基本策略:伪装User-Agent、登录、使用代理和降低访问频率。

1.通过User-Agent来控制访问

User-Agent是HTTP协议中的一个字段,其作用是描述发出HTTP请求的终端的一些信息。服务器通过这个字段就可以知道要访问的是哪个网站。对于各类浏览器,每个正规的爬虫都有其固定的User-Agent,因此只要将这个字段改为某些知名的User-Agent,就可以成功伪装了。不过,不推荐伪装知名爬虫,因为这些爬虫很可能有固定的IP,如百度爬虫。与此相对的,伪装浏览器的User-Agent是一个不错的主意,因为浏览器是任何人都可以用的,换名话说,就是没有固定IP。推荐准备若干个浏览器的User-Agent,然后每次发送请求时就从这几个User-Agent中随机选一个填上去。IE的几个User-Agent如下:

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)

Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)

可设置如下代码,这里使用的是JAVA+HttpClient 4.1.2。


HttpGet getMethod = new HttpGet("URL"); 
getMethod.setHeader("User-Agent", "user agent内容");

无论是浏览器还是爬虫程序,在向服务器发起网络请求的时候,都会发过去一个头文件headers,下面以百度的headers为例。

【例2-4】百度headers中的User-Agent访问控制举例

下述http的headers是通过Fiddler抓包获取的。


GET http://www.baidu.com/ HTTP/1.1
Accept-Encoding: identity
Host: www.baidu.com
User-Agent: Python-urllib/3.6
Connection: close

浏览器访问的headers信息如下:


GET https://www.baidu.com/ HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
User -Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, 
like Gecko) Chrome/62.0.3202.62 Safari/537.36
...

我们通过爬虫程序来访问第三方服务器时,服务器就能知道你是通过爬虫程序访问的,因为你的http的请求头信息User-Agent字段出卖了你。所以我们需要修改请求头的User-Agent字段信息,防止IP被禁。很多网站都会建立User-Agent白名单,只有属于正常范围的User-Agent才能够正常访问,下面是Python的User-Agent伪装代码。


# 定义一个url
url = 'http://www.baidu.com/'

# 定义一个请求头的User-Agent字段,其内容是通过Fiddler抓取的url的header信息
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36'}

# 自定义请求头信息,返回一个请求的对象request
request = urllib.request.Request(url,headers = headers)
# 通过urlopen访问url,服务器返回response对象
response = urllib.request.urlopen(request)
# 读取返回结果
result = response.read()

# 写入文件
with open('baidu.html','wb') as f:
    f.write(result)

2.登录

虽然有些网站不登录就能访问,但如果它检测到某IP的访问量有异常,那么就会马上提出登录要求。如果是不带验证码的,那么可以直接登录。不过,在登录之前要做些准备:查清楚POST登录请求时要附带哪些参数。先用Badboy录制登录过程(Badboy软件的学习地址:https://www.cnblogs.com/Lam7/p/5462536.html),然后将这一过程导出为Jmeter(Jmeter软件的学习地址:https://blog.csdn.net/zmeilin/article/details/93860839)文件,最后用Jmeter查看登录所需的参数。查完后就可以登录,具体如下所示。

【例2-5】登录设置示例


DefaultHttpClient httpclient = new DefaultHttpClient();         
HttpPost postMethod = new HttpPost("http://passport.cnblogs.com/login.aspx");  
//注意用post                  
//登录博客园所需的参数         
List nvps = new ArrayList();          
nvps.add(new BasicNameValuePair("tbUserName", "风炎"));           
nvps.add(new BasicNameValuePair("tbPassword", "zero"));         
nvps.add(new BasicNameValuePair("btnLogin", "登录"));         
nvps.add(new BasicNameValuePair("__EVENTTARGET", ""));         
nvps.add(new BasicNameValuePair("__EVENTARGUMENT", ""));         
nvps.add(new BasicNameValuePair("__VIEWSTATE", "/wEPDwULLTE1MzYzODg2NzZkGAEFHl9fQ29udHJvbHNSZXF1aXJlUG9zdEJhY2tLZXlfXxYBBQtjaGtSZW1lbWJlcm1QYDyKKI9af4b67Mzq2xFaL9Bt"));
nvps.add(new BasicNameValuePair("__EVENTVALIDATION", "/wEWBQLWwpqPDQLyj/OQAgK3jsrkBALR55GJDgKC3IeGDE1m7t2mGlasoP1Hd9hLaFoI2G05"));                
nvps.add(new BasicNameValuePair("ReturnUrl", "http://www.cnblogs.com/"));      
nvps.add(new BasicNameValuePair("txtReturnUrl", "http://www.cnblogs.com/"));           postMethod.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));           
HttpResponse response = httpclient.execute(postMethod);

如果一个固定的IP在短暂的时间内快速大量地访问一个网站,那自然会引起注意,管理员可以通过一些手段来禁止该IP的访问,即可限制反爬虫。

(1)通过JavaScript脚本来防止爬虫

这个办法可以非常有效地解决爬虫,爬虫终归只是一段程序,它不能像人一样去应对各种变化,例如验证码、滑动解锁等。有很多网站都采用了这种方式来避免爬虫,当请求量大了以后就会要求输入验证码的情况,我们平时使用的12306采用的便是验证码机制,可以在一定程度上防止非正当请求的产生。

(2)通过robots.txt来限制爬虫

世界上做爬虫最大最好的就是Google了,搜索引擎本身就是一个超级大的爬虫,Google开发出来的爬虫24小时不间断地在网上爬取着新的信息,并返回给数据库,但是这些搜索引擎的爬虫都遵守着一个协议——robots.txt。

robots.txt是一种存放于网站根目录下的ASCII编码的文本文件,它通常会告诉网络搜索引擎的漫游器(又称网络蜘蛛),此网站中的哪些内容是不应被搜索引擎的漫游器获取的,而哪些是可以被漫游器获取的。因为一些系统中的URL是大小写敏感的,所以robots.txt的文件名应统一为小写。robots.txt协议并不是一个规范,而只是约定俗成的。

3.使用代理

如果对方用某段时间内某IP的访问次数来判定爬虫,然后将这些爬虫的IP都封掉的话,以上伪装就失效了。对方的这个思路隐含着一个假设,即爬虫的访问量必然比正常用户的大很多,因而只要使这个假设不成立就可以。这时该代理就发挥了其作用。所谓代理就是介于用户与网站之间的第三者:用户先将请求发给代理,然后代理再发给服务器,这样看起来就像是代理在访问那个网站。这时,服务器会将这次访问算到代理头上。若同时使用多个代理的话,单个IP的访问量就降下去了。不过,这个方法最大的问题就是找到稳定的代理。

假设找到或买到了多个代理,那么要如何管理这些代理呢?我们可以做一个类似于内存池的IP池。这样做的好处是便于管理以及易于扩展。当只有一个代理时,其用法如下所示。

【例2-6】管理代理示例


DefaultHttpClient httpclient = new DefaultHttpClient();
    //此代理不保证你看到的时候还存活
HttpHost proxy = new HttpHost("u120-227.static.grapesc.cz", 8080);
httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,proxy);
    //记得将网址拆成以下形式
HttpHost targetHost = new HttpHost("www.cnblogs.com");
    //网站名前面不要加http:// 
HttpGet httpget = new HttpGet("/FengYan/");
HttpResponse response = httpclient.execute(targetHost, httpget);

4.降低访问频率

如果说找不到稳定的代理呢?那只好使用“降低访问频率”。这样做可以达到与使用代理一样的效果,以防止对方从访问量上看出来。当然,在抓取效率上会差很多。此外,降低访问频率只是一个指导思想,在这个思想下,可以得到很多具体的做法,例如,每抓取一个页面就休息随机秒、限制每天抓取的页面数量等。

由于爬虫在抓取网站数据的时候,对网站访问过于频繁,给服务器造成过大的压力,容易使网站崩溃,因此网站维护者会通过一些手段来避免爬虫的访问,以下是几种常见的反爬虫策略,如表2-1所示。

表2-1 反爬虫应对策略