天天看點

網站如何反爬蟲淺析

要想做爬蟲,必須要先了解如何反爬蟲,知道了這些,我們才能更好的做爬蟲!

因為搜尋引擎的流行,網絡爬蟲已經成了很普及網絡技術,除了專門做搜尋的Google,Yahoo,微軟,百度以外,幾乎每個大型門戶網站都有自己的搜尋引擎,大大小小叫得出來名字得就幾十種,還有各種不知名的幾千幾萬種,對于一個内容型驅動的網站來說,受到網絡爬蟲的光顧是不可避免的。

一些智能的搜尋引擎爬蟲的爬取頻率比較合理,對網站資源消耗比較少,但是很多糟糕的網絡爬蟲,對網頁爬取能力很差,經常并發幾十上百個請求循環重複抓取,這種爬蟲對中小型網站往往是毀滅性打擊,特别是一些缺乏爬蟲編寫經驗的程式員寫出來的爬蟲破壞力極強,造成的網站通路壓力會非常大,會導緻網站通路速度緩慢,甚至無法通路。

手工識别和拒絕爬蟲的通路

相當多的爬蟲對網站會造成非常高的負載,是以識别爬蟲的來源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段可以很大程度上解決問題。

通過識别爬蟲的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"]}"  

      

然後統計每天的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 falseend

      

這段程式隻是最簡單的示例,實際的代碼實作我們還會添加很多判斷,比方說我們可能要排除白名單IP位址段,要允許特定的User-Agent通過,要針對登入使用者和非登入使用者,針對有無referer位址采取不同的閥值和計數加速器等等。

此外如果分布式爬蟲爬取頻率過高的話,過期就允許爬蟲再次通路還是會對伺服器造成很大的壓力,是以我們可以添加一條政策:針對要求使用者填寫驗證碼的IP位址,如果該IP位址短時間内繼續不停的請求,則判斷為爬蟲,加入黑名單,後續請求全部拒絕掉。為此,示例代碼可以改進一下:

before_filter :ip_firewall, :except => :testdef 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 falseend

      

如果某個IP位址機關時間内通路頻率超過閥值,再增加一個計數器,跟蹤他會不會立刻填寫驗證碼,如果他不填寫驗證碼,在短時間内還是高頻率通路,就把這個IP位址段加入黑名單,除非使用者填寫驗證碼激活,否則所有請求全部拒絕。這樣我們就可以通過在程式裡面維護黑名單的方式來動态的跟蹤爬蟲的情況,甚至我們可以自己寫個背景來手工管理黑名單清單,了解網站爬蟲的情況。

關于這個通用反爬蟲的功能,我們開發一個開源的插件:https://github.com/csdn-dev/limiter

這個政策已經比較智能了,但是還不夠好!我們還可以繼續改進:

1、用網站流量統計系統來改進實時反爬蟲系統

還記得嗎?網站流量統計系統記錄的IP位址是真實使用者通路IP,是以我們在網站流量統計系統裡面也去操作memcached,但是這次不是增加計數值,而是減少計數值。在網站流量統計系統裡面每接收到一個IP請求,就相應的cache.decrement(key)。是以對于真實使用者的IP來說,它的計數值總是加1然後就減1,不可能很高。這樣我們就可以大大降低判斷爬蟲的閥值,可以更加快速準确的識别和拒絕掉爬蟲。

2、用時間視窗來改進實時反爬蟲系統

爬蟲爬取網頁的頻率都是比較固定的,不像人去通路網頁,中間的間隔時間比較無規則,是以我們可以給每個IP位址建立一個時間視窗,記錄IP位址最近12次通路時間,每記錄一次就滑動一次視窗,比較最近通路時間和目前時間,如果間隔時間很長判斷不是爬蟲,清除時間視窗,如果間隔不長,就回溯計算指定時間段的通路頻率,如果通路頻率超過閥值,就轉向驗證碼頁面讓使用者填寫驗證碼。

最終這個實時反爬蟲系統就相當完善了,它可以很快的識别并且自動封鎖爬蟲的通路,保護網站的正常通路。不過有些爬蟲可能相當狡猾,它也許會通過大量的爬蟲測試來試探出來你的通路閥值,以低于閥值的爬取速度抓取你的網頁,是以我們還需要輔助第3種辦法,用日志來做後期的分析和識别,就算爬蟲爬的再慢,它累計一天的爬取量也會超過你的閥值被你日志分析程式識别出來。

總之我們綜合運用上面的四種反爬蟲政策,可以很大程度上緩解爬蟲對網站造成的負面影響,保證網站的正常通路。