由于搜索引擎的流行,网络爬虫已经成了很普及网络技术,除了专门作搜索的Google,Yahoo,微软,百度之外,几乎每一个大型门户网站都有本身的搜索引擎,大大小小叫得出来名字得就几十种,还有各类不知名的几千几万种,对于一个内容型驱动的网站来讲,受到网络爬虫的光顾是不可避免的。html
一些智能的搜索引擎爬虫的爬取频率比较合理,对网站资源消耗比较少,可是不少糟糕的网络爬虫,对网页爬取能力不好,常常并发几十上百个请求循环重复抓取,这种爬虫对中小型网站每每是毁灭性打击,特别是一些缺少爬虫编写经验的程序员写出来的爬虫破坏力极强,形成的网站访问压力会很是大,会致使网站访问速度缓慢,甚至没法访问。java
至关多的爬虫对网站会形成很是高的负载,所以识别爬虫的来源IP是很容易的事情。最简单的办法就是用netstat检查80端口的链接:git
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了。github
拒绝爬虫请求既能够经过内核防火墙来拒绝,也能够在web server拒绝,比方说用iptables拒绝:web
iptables -A INPUT -i eth0 -j DROP -p tcp --dport 80 -s 84.80.46.0/24
直接封锁爬虫所在的C网段地址。这是由于通常爬虫都是运行在托管机房里面,可能在一个C段里面的多台服务器上面都有爬虫,而这个C段不多是用户宽带上网,封锁C段能够很大程度上解决问题。shell
有不少爬虫并不会以很高的并发链接爬取,通常不容易暴露本身;有些爬虫的来源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"]}"
而后统计天天的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
统计结果相似这样:
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类库的User-Agent信息,这样就能够避免不少无谓的程序员用来练手的爬虫程序对网站的骚扰。
还有一种比较常见的状况,就是某个搜索引擎的爬虫对网站爬取频率太高,可是搜索引擎给网站带来了不少流量,咱们并不但愿简单的封锁爬虫,仅仅是但愿下降爬虫的请求频率,减轻爬虫对网站形成的负载,那么咱们能够这样作:
$HTTP["user-agent"] =~ "Baiduspider+" { connection.delay-seconds = 10 }
对百度的爬虫请求延迟10秒钟再进行处理,这样就能够有效下降爬虫对网站的负载了。
有些爬虫喜欢修改User-Agent信息来假装本身,把本身假装成一个真实浏览器的User-Agent信息,让你没法有效的识别。这种状况下咱们能够经过网站流量系统记录的真实用户访问IP来进行识别。
主流的网站流量统计系统不外乎两种实现策略:一种策略是在网页里面嵌入一段js,这段js会向特定的统计服务器发送请求的方式记录访问量;另外一种策略是直接分析服务器日志,来统计网站访问量。在理想的状况下,嵌入js的方式统计的网站流量应该高于分析服务器日志,这是由于用户浏览器会有缓存,不必定每次真实用户访问都会触发服务器的处理。但实际状况是,分析服务器日志获得的网站访问量远远高于嵌入js方式,极端状况下,甚至要高出10倍以上。
如今不少网站喜欢采用awstats来分析服务器日志,来计算网站的访问量,可是当他们一旦采用Google Analytics来统计网站流量的时候,却发现GA统计的流量远远低于awstats,为何GA和awstats统计会有这么大差别呢?罪魁祸首就是把本身假装成浏览器的网络爬虫。这种状况下awstats没法有效的识别了,因此awstats的统计数据会虚高。
其实做为一个网站来讲,若是但愿了解本身的网站真实访问量,但愿精确了解网站每一个频道的访问量和访问用户,应该用页面里面嵌入js的方式来开发本身的网站流量统计系统。本身作一个网站流量统计系统是件很简单的事情,写段服务器程序响应客户段js的请求,分析和识别请求而后写日志的同时作后台的异步统计就搞定了。
经过流量统计系统获得的用户IP基本是真实的用户访问,由于通常状况下爬虫是没法执行网页里面的js代码片断的。因此咱们能够拿流量统计系统记录的IP和服务器程序日志记录的IP地址进行比较,若是服务器日志里面某个IP发起了大量的请求,在流量统计系统里面却根本找不到,或者即便找获得,可访问量却只有寥寥几个,那么无疑就是一个网络爬虫。
分析服务器日志统计访问最多的IP地址段一行shell就能够了:
grep Processing production.log | awk '{print $4}' | awk -F'.' '{print $1"."$2"."$3".0"}' | sort | uniq -c | sort -r -n | head -n 200 > stat_ip.log
而后把统计结果和流量统计系统记录的IP地址进行对比,排除真实用户访问IP,再排除咱们但愿放行的网页爬虫,比方Google,百度,微软msn爬虫等等。最后的分析结果就就获得了爬虫的IP地址了。如下代码段是个简单的实现示意:
whitelist = [] IO.foreach("#{RAILS_ROOT}/lib/whitelist.txt") { |line| whitelist << line.split[0].strip if line } realiplist = [] IO.foreach("#{RAILS_ROOT}/log/visit_ip.log") { |line| realiplist << line.strip if line } iplist = [] IO.foreach("#{RAILS_ROOT}/log/stat_ip.log") do |line| ip = line.split[1].strip iplist << ip if line.split[0].to_i > 3000 && !whitelist.include?(ip) && !realiplist.include?(ip) end Report.deliver_crawler(iplist)
分析服务器日志里面请求次数超过3000次的IP地址段,排除白名单地址和真实访问IP地址,最后获得的就是爬虫IP了,而后能够发送邮件通知管理员进行相应的处理。
经过分析日志的方式来识别网页爬虫不是一个实时的反爬虫策略。若是一个爬虫非要针对你的网站进行处心积虑的爬取,那么他可能会采用分布式爬取策略,比方说寻找几百上千个国外的代理服务器疯狂的爬取你的网站,从而致使网站没法访问,那么你再分析日志是不可能及时解决问题的。因此必须采起实时反爬虫策略,要可以动态的实时识别和封锁爬虫的访问。
要本身编写一个这样的实时反爬虫系统其实也很简单。比方说咱们能够用memcached来作访问计数器,记录每一个IP的访问频度,在单位时间以内,若是访问频率超过一个阀值,咱们就认为这个IP极可能有问题,那么咱们就能够返回一个验证码页面,要求用户填写验证码。若是是爬虫的话,固然不可能填写验证码,因此就被拒掉了,这样很简单就解决了爬虫问题。
用memcache记录每一个IP访问计数,单位时间内超过阀值就让用户填写验证码,用Rails编写的示例代码以下:
ip_counter = Rails.cache.increment(request.remote_ip) if !ip_counter Rails.cache.write(request.remote_ip, 1, :expires_in => 30.minutes) elsif ip_counter > 2000 render :template => 'test', :status => 401 and return false end
这段程序只是最简单的示例,实际的代码实现咱们还会添加不少判断,比方说咱们可能要排除白名单IP地址段,要容许特定的User-Agent经过,要针对登陆用户和非登陆用户,针对有无referer地址采起不一样的阀值和计数加速器等等。
此外若是分布式爬虫爬取频率太高的话,过时就容许爬虫再次访问仍是会对服务器形成很大的压力,所以咱们能够添加一条策略:针对要求用户填写验证码的IP地址,若是该IP地址短期内继续不停的请求,则判断为爬虫,加入黑名单,后续请求所有拒绝掉。为此,示例代码能够改进一下:
before_filter :ip_firewall, :except => :test def ip_firewall render :file => "#{RAILS_ROOT}/public/403.html", :status => 403 if BlackList.include?(ip_sec) end
咱们能够定义一个全局的过滤器,对全部请求进行过滤,出如今黑名单的IP地址一概拒绝。对非黑名单的IP地址再进行计数和统计:
ip_counter = Rails.cache.increment(request.remote_ip) if !ip_counter Rails.cache.write(request.remote_ip, 1, :expires_in => 30.minutes) elsif ip_counter > 2000 crawler_counter = Rails.cache.increment("crawler/#{request.remote_ip}") if !crawler_counter Rails.cache.write("crawler/#{request.remote_ip}", 1, :expires_in => 10.minutes) elsif crawler_counter > 50 BlackList.add(ip_sec) render :file => "#{RAILS_ROOT}/public/403.html", :status => 403 and return false end render :template => 'test', :status => 401 and return false end
若是某个IP地址单位时间内访问频率超过阀值,再增长一个计数器,跟踪他会不会马上填写验证码,若是他不填写验证码,在短期内仍是高频率访问,就把这个IP地址段加入黑名单,除非用户填写验证码激活,不然全部请求所有拒绝。这样咱们就能够经过在程序里面维护黑名单的方式来动态的跟踪爬虫的状况,甚至咱们能够本身写个后台来手工管理黑名单列表,了解网站爬虫的状况。
关于这个通用反爬虫的功能,咱们开发一个开源的插件:https://github.com/csdn-dev/limiter
这个策略已经比较智能了,可是还不够好!咱们还能够继续改进:
一、用网站流量统计系统来改进实时反爬虫系统
还记得吗?网站流量统计系统记录的IP地址是真实用户访问IP,因此咱们在网站流量统计系统里面也去操做memcached,可是此次不是增长计数值,而是减小计数值。在网站流量统计系统里面每接收到一个IP请求,就相应的cache.decrement(key)。因此对于真实用户的IP来讲,它的计数值老是加1而后就减1,不可能很高。这样咱们就能够大大下降判断爬虫的阀值,能够更加快速准确的识别和拒绝掉爬虫。
二、用时间窗口来改进实时反爬虫系统
爬虫爬取网页的频率都是比较固定的,不像人去访问网页,中间的间隔时间比较无规则,因此咱们能够给每一个IP地址创建一个时间窗口,记录IP地址最近12次访问时间,每记录一次就滑动一次窗口,比较最近访问时间和当前时间,若是间隔时间很长判断不是爬虫,清除时间窗口,若是间隔不长,就回溯计算指定时间段的访问频率,若是访问频率超过阀值,就转向验证码页面让用户填写验证码。
最终这个实时反爬虫系统就至关完善了,它能够很快的识别而且自动封锁爬虫的访问,保护网站的正常访问。不过有些爬虫可能至关狡猾,它也许会经过大量的爬虫测试来试探出来你的访问阀值,以低于阀值的爬取速度抓取你的网页,所以咱们还须要辅助第3种办法,用日志来作后期的分析和识别,就算爬虫爬的再慢,它累计一天的爬取量也会超过你的阀值被你日志分析程序识别出来。
总之咱们综合运用上面的四种反爬虫策略,能够很大程度上缓解爬虫对网站形成的负面影响,保证网站的正常访问。