網站反爬蟲

由於搜索引擎的流行,網絡爬蟲已經成了很普及網絡技術,除了專門作搜索的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

經過識別爬蟲的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 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種辦法,用日誌來作後期的分析和識別,就算爬蟲爬的再慢,它累計一天的爬取量也會超過你的閥值被你日誌分析程序識別出來。

總之咱們綜合運用上面的四種反爬蟲策略,能夠很大程度上緩解爬蟲對網站形成的負面影響,保證網站的正常訪問。    

轉:http://blog.csdn.net/huaweitman/article/details/9714981

相關文章
相關標籤/搜索