1021080433
(更新时间:2026-08-31 15:19:09)
解决方案

网络网站的反爬虫方案浅谈

发表日期:2025-12-22   作者来源:www.lndmf.com   浏览:246   标签:    

由于搜索引擎的时尚,互联网爬虫已经成了非常普及互联网技术,除去专门做搜索的谷歌,Yahoo,Microsoft,百度以外,几乎每一个大型门户网站都有我们的搜索引擎,大大小小叫得出来名字得就几十种,还有各种不知名的几千几万种,对于一个内容型驱动的网站来讲,遭到互联网爬虫的光顾是不可防止的。

一些智能的搜索引擎爬虫的爬取频率比较合理,对网站资源消耗比较少,但不少糟糕的互联网爬虫,对网页爬取能力非常差,常常并发几十上百个请求循环重复抓取,这种爬虫对中小型网站总是是毁灭性打击,尤其是一些缺少爬虫撰写经验的技术员写出来的爬虫破坏力最强。过去有一次我在JavaEye的日志里面发现一个User|Agent是Java的爬虫一天之内爬取了将近100万次动态请求。这是一个用JDK标准类库撰写的简单爬取网页程序,因为JavaEye网站内链构成了回环致使程序陷入了死循环。对于JavaEye这种百万PV级别的网站来讲,这种爬虫导致的访问重压会很大,会致使网站访问速度缓慢,甚至没办法访问。

除此之外,相当数目的的网页爬虫目的是盗取目的网站的内容。比如JavaEye网站就过去被两个角逐对手网站爬取平台帖子,然后在我们的平台里面用机器人发帖,因此这种爬虫不只影响网站访问速度,而且侵有网站的版权。

对于一个原创内容丰富,URL结构合理易于爬取的网站来讲,简直就是各种爬虫的盘中大餐,不少网站的访问流量构成当中,爬虫带来的流量要远远超越真实用户访问流量,甚至爬虫流量要高出真实流量一个数目级。像JavaEye网站虽然设置了相当严格的反爬虫方案,但网站处置的动态请求数目仍然是真实用户访问流量的2倍。可以一定的说,当今网络的互联网流量至少有2/3的流量爬虫带来的。因此反爬虫是一个值得网站长期探索和解决的问题。

1、手工辨别和拒绝爬虫的访问

有相当多的爬虫对网站会导致特别高的负载,因此辨别爬虫的来源IP是比较容易的事情。简单的方法就是用netstat检查80端口的连接:

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

直接封锁爬虫所在的C网段地址。这是由于一般爬虫都是运行在推广托管机房里面,可能在一个C段里面的多台服务器上面都有爬虫,而这个C段不可能是用户宽带上网,封锁C段可以非常大程度上解决问题。

有的人提出一种脑残的看法,说我要惩罚这类爬虫。我专门在网页里面设计动态循环链接页面,让爬虫掉进陷阱,死循环爬不出来,其实根本用不着设置陷阱,弱智爬虫对正常网页自己就爬不出来,如此做画蛇添足不说,而且会让真正的搜索引擎减少你的网页排名。而且运行一个爬虫根本不消耗什么机器资源,相反,真正宝贵的是你的服务器CPU资源和服务器带宽,简单的拒绝掉爬虫的请求是反爬虫有效的方案。

2、通过辨别爬虫的User|Agent信息来拒绝爬虫

有不少爬虫并不会以非常高的并发连接爬取,一般困难暴露自己;有的爬虫的来源IP分布非常广,非常难简单的通过封锁IP段地址来解决问题;另外还有不少各种各样的小爬虫,它们在尝试谷歌以外革新的搜索方法,每一个爬虫天天爬取几万的网页,几十个爬虫加起来天天就能消耗掉上百万动态请求的资源,因为每一个小爬虫单独的爬取量都非常低,所以你非常难把它从天天大量的访问IP地址当中把它准确的挖出来。[Page]

这样的情况下大家可以通过爬虫的User|Agent信息来辨别。每一个爬虫在爬取网页的时候,会声明我们的User|Agent信息,因此大家就能通过记录和剖析User|Agent信息来挖掘和封锁爬虫。大家需要记录每一个请求的User|Agent信息,对于Rails来讲大家可以简单的在app/controllers/application.rb里面添加一个全局的before|filter,来记录每一个请求的User|Agent信息:

Ruby代码

logger.info "HTTP|USER|AGENT #{request.env["HTTP|USER|AGENT"]}"

然后统计天天的production.log,抽取User|Agent信息,找出访问量大的那些User|Agent。应该注意的是大家只关注那些爬虫的User|Agent信息,而不是真正浏览器User|Agent,所以还要排除掉浏览器User|Agent,要做到这一点仅只需一行shell:

Ruby代码

grep HTTP|USER|AGENT production.log | grep |v |E 'MSIE|Firefox|Chrome|Opera|Safari|Gecko' | sort | uniq |c | sort |r |n | head |n 100bot.log

统计结果类似如此:

C代码

57335 HTTP|USER|AGENT Baiduspider+(+http://www.baidu.com/search/spider.htm) 56639 HTTP|USER|AGENT Mozilla/5.0 (compatible; 谷歌bot/2.1; +http://www.谷歌.com/bot.html) 42610 HTTP|USER|AGENT Mediapartners|谷歌 19131 HTTP|USER|AGENT msnbot/2.0b (+http://search.msn.com/msnbot.htm)

从日志就能直观的看出每一个爬虫的请求次数。要依据User|Agent信息来封锁爬虫是件比较容易的事情,lighttpd配置如下:

C代码

$HTTP["useragent"] =~ "qihoobot|^Java|Commons|HttpClient|Wget|^PHP|Ruby|Python" {url.rewrite = ( "^/(.*)" = "/crawler.html" ) }

用这种方法来封锁爬虫虽然简单但很有效,除去封锁特定的爬虫,还可以封锁常见的编程语言和HTTP类库的User|Agent信息,如此就能防止不少无谓的技术员用来练手的爬虫程序对网站的骚扰。

还有一种经常见到的状况,就是某个搜索引擎的爬虫对网站爬取频率过高,但搜索引擎给网站带来了不少流量,大家并不期望简单的封锁爬虫,只是期望减少爬虫的请求频率,减轻爬虫对网站导致的负载,那样大家可以如此做:

C代码

$HTTP["user|agent"] =~ "Baiduspider+" {connection.delay|seconds = 10 }

对百度的爬虫请求延迟10秒钟再进行处置,如此就能大大降低爬虫对网站的负载了。

3、通过网站流量统计系统和日志剖析来辨别爬虫

有的爬虫喜欢修改User|Agent信息来伪装自己,把自己伪装成一个真实浏览器的User|Agent信息,叫你没办法有效的辨别。这样的情况下大家可以通过网站流量系统记录的真实用户访问IP来进行辨别。

主流的网站流量统计系统不外乎两种达成方案:一种方案是在网页里面嵌入一段js,这段js会向特定的统计服务器发送请求的方法记录访问量;另一种方案是直接剖析服务器日志,来统计网站访问量。在理想的状况下,嵌入js的方法统计的网站流量应该高于剖析服务器日志,这是由于用户浏览器会有缓存,未必每次真实用户访问都会触发服务器的处置。但实质状况是,剖析服务器日志得到的网站访问量远远高于嵌入js方法,极端状况下,甚至要高出10倍以上。

目前不少网站喜欢使用awstats来剖析服务器日志,来计算网站的访问量,但当他们一旦使用谷歌分析来统计网站流量的时候,却发现GA统计的流量远远低于awstats,为何GA和awstats统计会有这么大差异呢?罪魁祸首就是把自己伪装成浏览器的互联网爬虫。这样的情况下awstats没办法有效的辨别了,所以awstats的统计数据会虚高。[Page]

其实作为一个网站来讲,假如期望知道我们的网站真实访问量,期望精准知道网站每一个频道的访问量和访问用户,应该用页面里面嵌入js的方法来开发我们的网站流量统计系统。自己做一个网站流量统计系统是件非常简单的事情,写段服务器程序响应顾客段js的请求,剖析和辨别请求然后写日志的同时做后台的异步统计就解决了。

通过流量统计系统得到的用户IP基本是真实的用户访问,由于通常情况下爬虫是没办法实行网页里面的js代码片段的。所以大家可以拿流量统计系统记录的IP和服务器程序日志记录的IP地址进行比较,假如服务器日志里面某个IP发起了很多的请求,在流量统计系统里面却根本找不到,或者即便找得到,可访问量却只有寥寥几个,那样无疑就是一个互联网爬虫。

剖析服务器日志统计访问多的IP地址段一行shell就能了:

C代码

grep Processing production.log | awk '{print $4}' | awk |F'.' '{print $1"."$2"."$3".0"}' | sort | uniq |c | sort |r |n | head |n 200stat|ip.log

然后把统计结果和流量统计系统记录的IP地址进行对比,排除真实用户访问IP,再排除大家期望放行的网页爬虫,比方谷歌,百度,Microsoftmsn爬虫等等。后的剖析结果就就得到了爬虫的IP地址了。以下代码段是个简单的达成示意:

Ruby代码

whitelist = [] IO.foreach("#{R人工智能LS|ROOT}/lib/whitelist.txt") { |line| whitelistline.split[0].strip if line }realiplist = [] IO.foreach("#{R人工智能LS|ROOT}/log/visit|ip.log") { |line| realiplistline.strip if line }iplist = [] IO.foreach("#{R人工智能LS|ROOT}/log/stat|ip.log") do |line|ip = line.split[1].stripiplistip if line.split[0].to|i3000!whitelist.include?(ip)!realiplist.include?(ip) endReport.deliver|crawler(iplist)

剖析服务器日志里面请求次数超越3000次的IP地址段,排除白名单地址和真实访问IP地址,后得到的就是爬虫IP了,然后可以发送邮件公告管理员进行相应的处置。

4、网站的实时反爬虫防火墙达成方案

通过剖析日志的方法来辨别网页爬虫不是一个实时的反爬虫方案。假如一个爬虫非要针对你的网站进行费尽心机的爬取,那样他或许会使用分布式爬取方案,比如探寻几百上千个海外的代理服务器疯狂的爬取你的网站,从而致使网站没办法访问,那样你再剖析日志是不可能准时解决问题的。所以需要采取实时反爬虫方案,要可以动态的实时辨别和封锁爬虫的访问。

要自己撰写一个如此的实时反爬虫系统其实也非常简单。比如大家可以用memcached来做访问计数器,记录每一个IP的访问频度,在单位时间之内,假如访问频率超越一个阀值,大家就觉得这个IP非常可能有问题,那样大家就能返回一个验证码页面,需要用户填写验证码。若是爬虫的话,当然不可能填写验证码,所以就被拒掉了,如此非常简单就解决了爬虫问题。

用memcache记录每一个IP访问计数,单位时间内超越阀值就让用户填写验证码,用Rails撰写的示例代码如下:

Ruby代码

ip|counter = Rails.cache.increment(request.remote|ip) if !ip|counterRails.cache.write(request.remote|ip, 1, :expires|in = 30.minutes) elsif ip|counter2000render :template = 'test', :status = 401 and return false [Page]end

这段程序只不过简单的示例,实质的代码达成大家还会添加不少判断,比如大家可能要排除白名单IP地址段,要允许特定的User|Agent通过,要针对登录用户和非登录用户,针对有无referer地址采取不一样的阀值和计数加速器等等。

除此之外假如分布式爬虫爬取频率过高的话,过期就允许爬虫第三访问还是会对服务器导致非常大的重压,因此大家可以添加一条方案:针对需要用户填写验证码的IP地址,假如该IP地址短期内继续不停的请求,则判断为爬虫,加入黑名单,后续请求全部拒绝掉。为此,示例代码可以改进一下:

Ruby代码

before|filter :ip|firewall, :except = :test def ip|firewallrender :file = "#{R人工智能LS|ROOT}/public/403.html", :status = 403 if BlackList.include?(ip|sec) end

大家可以概念一个全局的过滤器,对所有请求进行过滤,出目前黑名单的IP地址一律拒绝。对非黑名单的IP地址再进行计数和统计:

Ruby代码

ip|counter = Rails.cache.increment(request.remote|ip) if !ip|counterRails.cache.write(request.remote|ip, 1, :expires|in = 30.minutes) elsif ip|counter2000crawler|counter = Rails.cache.increment("crawler/#{request.remote|ip}")if !crawler|counterRails.cache.write("crawler/#{request.remote|ip}", 1, :expires|in = 10.minutes)elsif crawler|counter50BlackList.add(ip|sec)render :file = "#{R人工智能LS|ROOT}/public/403.html", :status = 403 and return falseendrender :template = 'test', :status = 401 and return false end

假如某个IP地址单位时间内访问频率超越阀值,再增加一个计数器,跟踪他是否会立刻填写验证码,假如他不填写验证码,在短期内还是高频率访问,就把这个IP地址段加入黑名单,除非用户填写验证码激活,不然所有请求全部拒绝。如此大家就能通过在程序里面维护黑名单的方法来动态的跟踪爬虫的状况,甚至大家可以自己写个后台来手工管理黑名单列表,知道网站爬虫的状况。

这个方案已经比较智能了,但还不够好!大家还可以继续改进:

1、用网站流量统计系统来改进实时反爬虫系统

还记得吗?网站流量统计系统记录的IP地址是真实用户访问IP,所以大家在网站流量统计系统里面也去操作memcached,但这次不是增加计数值,而是降低计数值。在网站流量统计系统里面每接收到一个IP请求,就相应的cache.decrement(key)。所以对于真实用户的IP来讲,它的计数值一直加1然后就减1,不可能非常高。如此大家就能大大减少判断爬虫的阀值,可以愈加迅速准确的辨别和拒绝掉爬虫。

2、用时间窗口来改进实时反爬虫系统

爬虫爬取网页的频率都是比较固定的,不像人去访问网页,中间的间隔时间比较无规则,所以大家可以给每一个IP地址打造一个时间窗口,记录IP地址近12次访问时间,每记录一次就滑动一次窗口,比较近访问时间和目前时间,假如间隔时间非常长判断不是爬虫,清除时间窗口,假如间隔不长,就回溯计算指定时间段的访问频率,假如访问频率超越阀值,就转向验证码页面让用户填写验证码。

终这个实时反爬虫系统就相当健全了,它可以非常快的辨别并且自动封锁爬虫的访问,保护网站的正常访问。不过有的爬虫可能相当狡猾,它或许会通过很多的爬虫测试来试探出来你的访问阀值,以低于阀值的爬取速度抓取你的网页,因此大家还需要辅助第3种方法,用日志来做后期的剖析和辨别,即使爬虫爬的再慢,它累计一天的爬取量也会超越你的阀值被你日志剖析程序辨别出来。[Page]

如没特殊注明,文章均为厦门做网站的公司 原创,转载请注明来自https://www.scksm.com/news/list/8/https://www.scksm.com/news/fangan/425.html