因为搜索引擎的流行,网络爬虫已经成了很普及网络技术,除了专门做搜索的Google,Yahoo,微软,百度以外,几乎每个大型门户网站都有自己的搜索引擎,大大小小叫得出来名字得就几十种,还有各种不知名的几千几万种,对于一个内容型驱动的网站来说,受到网络爬虫的光顾是不可避免的。
一些智能的搜索引擎爬虫的爬取频率比较合理,对网站资源消耗比较少,但是很多糟糕的网络爬虫,对网页爬取能力很差,经常并发几十上百个请求循环重复抓取,这种爬虫对中小型网站往往是毁灭性打击,特别是一些缺乏爬虫编写经验的程序员写出来的爬虫破坏力极强。曾经有一次我在
JavaEye的日志里面发现一个User-Agent是Java的爬虫一天之内爬取了将近100万次动态请求。这是一个用JDK标准类库编写的简单爬取网页程序,由于JavaEye网站内部链接构成了回环导致程序陷入了死循环。对于JavaEye这种百万PV级别的网站来说,这种爬虫造成的访问压力会非常大,会导致网站访问速度缓慢,甚至无法访问。
此外,相当数量的的网页爬虫目的是盗取目标网站的内容。比方说JavaEye网站就曾经被两个竞争对手网站爬取论坛帖子,然后在自己的论坛里面用机器人发帖,因此这种爬虫不仅仅影响网站访问速度,而且侵犯了网站的版权。
对于一个原创内容丰富,URL结构合理易于爬取的网站来说,简直就是各种爬虫的盘中大餐,很多网站的访问流量构成当中,爬虫带来的流量要远远超过真实用户访问流量,甚至爬虫流量要高出真实流量一个数量级。像JavaEye网站虽然设置了相当严格的反爬虫策略,但是网站处理的动态请求数量仍然是真实用户访问流量的2倍。可以肯定的说,当今互联网的网络流量至少有2/3的流量爬虫带来的。因此反爬虫是一个值得网站长期探索和解决的问题。
一、手工识别和拒绝爬虫的访问
有相当多的爬虫对网站会造成非常高的负载,因此识别爬虫的来源IP是很容易的事情。最简单的办法就是用netstat检查80端口的连接:
- netstat -nt | grep youhostip:80 | awk '{print $5}' | awk -F":" '{print $1}'| sort | uniq -c | sort -r -n
netstat -nt | grep youhostip:80 | awk '{print $5}' | awk -F":" '{print $1}'| sort | uniq -c | sort -r -n
这行shell可以按照80端口连接数量对来源IP进行排序,这样可以直观的判断出来网页爬虫。一般来说爬虫的并发连接非常高。
如果使用lighttpd做Web Server,那么就更简单了。lighttpd的mod_status提供了非常直观的并发连接的信息,包括每个连接的来源IP,访问的URL,连接状态和连接时间等信息,只要检查那些处于handle-request状态的高并发IP就可以很快确定爬虫的来源IP了。
拒绝爬虫请求既可以通过内核防火墙来拒绝,也可以在web server拒绝,比方说用iptables拒绝:
- iptables -A INPUT -i eth0 -j DROP -p tcp --dport 80 -s 84.80.46.0/24
iptables -A INPUT -i eth0 -j DROP -p tcp --dport 80 -s 84.80.46.0/24
直接封锁爬虫所在的C网段地址。这是因为一般爬虫都是运行在托管机房里面,可能在一个C段里面的多台服务器上面都有爬虫,而这个C段不可能是用户宽带上网,封锁C段可以很大程度上解决问题。
有些人提出一种脑残的观点,说我要惩罚这些爬虫。我专门在网页里面设计动态循环链接页面,让爬虫掉进陷阱,死循环爬不出来,其实根本用不着设置陷阱,弱智爬虫对正常网页自己就爬不出来,这样做多此一举不说,而且会让真正的搜索引擎降低你的网页排名。而且运行一个爬虫根本不消耗什么机器资源,相反,真正宝贵的是你的服务器CPU资源和服务器带宽,简单的拒绝掉爬虫的请求是反爬虫最有效的策略。
二、通过识别爬虫的User-Agent信息来拒绝爬虫
有很多爬虫并不会以很高的并发连接爬取,一般不容易暴露自己;有些爬虫的来源IP分布很广,很难简单的通过封锁IP段地址来解决问题;另外还有很多各种各样的小爬虫,它们在尝试Google以外创新的搜索方式,每个爬虫每天爬取几万的网页,几十个爬虫加起来每天就能消耗掉上百万动态请求的资源,由于每个小爬虫单独的爬取量都很低,所以你很难把它从每天海量的访问IP地址当中把它准确的挖出来。
这种情况下我们可以通过爬虫的User-Agent信息来识别。每个爬虫在爬取网页的时候,会声明自己的User-Agent信息,因此我们就可以通过记录和分析User-Agent信息来挖掘和封锁爬虫。我们需要记录每个请求的User-Agent信息,对于Rails来说我们可以简单的在app/controllers/application.rb里面添加一个全局的before_filter,来记录每个请求的User-Agent信息:
- logger.info "HTTP_USER_AGENT #{request.env["HTTP_USER_AGENT"]}"
logger.info "HTTP_USER_AGENT #{request.env["HTTP_USER_AGENT"]}"
然后统计每天的production.log,抽取User-Agent信息,找出访问量最大的那些User-Agent。要注意的是我们只关注那些爬虫的User-Agent信息,而不是真正浏览器User-Agent,所以还要排除掉浏览器User-Agent,要做到这一点仅仅需要一行shell:
- grep HTTP_USER_AGENT production.log | grep -v -E 'MSIE|Firefox|Chrome|Opera|Safari|Gecko' | sort | uniq -c | sort -r -n | head -n 100 > bot.log
grep HTTP_USER_AGENT production.log | grep -v -E 'MSIE|Firefox|Chrome|Opera|Safari|Gecko' | sort | uniq -c | sort -r -n | head -n 100 > bot.log
统计结果类似这样:
- 57335 HTTP_USER_AGENT Baiduspider+(+http:
- 56639 HTTP_USER_AGENT Mozilla/5.0 (compatible; Googlebot/2.1; +http:
- 42610 HTTP_USER_AGENT Mediapartners-Google
- 19131 HTTP_USER_AGENT msnbot/2.0b (+http:
57335 HTTP_USER_AGENT Baiduspider+(+http://www.baidu.com/search/spider.htm)
56639 HTTP_USER_AGENT Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
42610 HTTP_USER_AGENT Mediapartners-Google
19131 HTTP_USER_AGENT msnbot/2.0b (+http://search.msn.com/msnbot.htm)
从日志就可以直观的看出每个爬虫的请求次数。要根据User-Agent信息来封锁爬虫是件很容易的事情,lighttpd配置如下:
- $HTTP["useragent"] =~ "qihoobot|^Java|Commons-HttpClient|Wget|^PHP|Ruby|Python" {
- url.rewrite = ( "^/(.*)" => "/crawler.html" )
- }
$HTTP["useragent"] =~ "qihoobot|^Java|Commons-HttpClient|Wget|^PHP|Ruby|Python" {
url.rewrite = ( "^/(.*)" => "/crawler.html" )
}
使用这种方式来封锁爬虫虽然简单但是非常有效,除了封锁特定的爬虫,还可以封锁常用的编程语言和HTTP类库的User-Agent信息,这样就可以避免很多无谓的程序员用来练手的爬虫程序对网站的骚扰。
还有一种比较常见的情况,就是某个搜索引擎的爬虫对网站爬取频率过高,但是搜索引擎给网站带来了很多流量,我们并不希望简单的封锁爬虫,仅仅是
希望降低爬虫的请求频率,减轻爬虫对网站造成的负载,那么我们可以这样做:
- $HTTP["user-agent"] =~ "Baiduspider+" {
- connection.delay-seconds = 10
- }
$HTTP["user-agent"] =~ "Baiduspider+" {
connection.delay-seconds = 10
}