crawler_網絡爬蟲中編碼的正確處理與亂碼的解決策略

 

轉載: http://hi.baidu.com/erliang20088/item/9156132bdaeae8949c63d134java

 最近一個月一直在對nutch1.6版進行中等層次的二次開發,原本是想從新作一個自寫的爬蟲系統,鑑於前基作過微博爬蟲系統,感受再重寫一個完整的爬蟲費時、費力還沒太大的含金量,故而直接基於nutch開發。web

    之因此說中是由於沒有改動nutch的核心部分map/reduce,但改動了除此以外的絕大部分問題,最終造成了任務提交多樣化、調度合理、數據流優化、亂碼處理、源碼與正文保存等較爲合理的網絡爬蟲系統。通過處理以後,本爬蟲能夠採集中文、繁體、英文、藏文、俄文、日文、西里爾文等多種文字。現就網絡爬蟲中常常遇到的亂碼問題,給予簡要總結。windows

    若是直接用nutch提供的web頁面(nutch1.2版及以前是有自帶的web展現系統,但以後的版本就沒有了)就會很容易發現,老是會出現或多或少的頁面。當深刻源碼後你會發現,nutch源碼中對源網頁中的編碼的識別是很淺的,統一用utf-8來處理,沒有作相關的編碼的精準識別,只是留了相應的接口,供二次開發人員自行添加如cpdetector之類的基於統計的編碼識別方式。數組

    本系統的二次開發,主要採用截取nutch數據流的開始和結尾兩部分數據來作,即提交數據、網頁源碼的數據兩部分來作。網頁經過閱讀源碼的網絡IO代碼會發現,http網頁源碼的流的讀出是經過socket鏈接,獲得其inputstream輸入流以後,以字節流的方來來讀的。瀏覽器

     這裏有個重點:若是源網頁是GBK字節流,在程序端接收時的inputstream獲得的字節數組的編碼方式確定是GBK字節流,即源網頁是什麼編碼方式的字節流,程序端接收到的字節流的編碼方式確定是相同的。所以,只要在程序端解析出該流實際的編碼方式便可將該流得到的源網頁的字節數組轉化成正常的編碼顯示形式。即算「解碼--解析編碼」的過程。網絡

     解析字節流的編碼主要有三種方式,socket

         一,經過http header中的content_type中的charset來得到,該編碼是最準確的。學習

         二,經過獲得源網頁的meta的charset來得到編碼。測試

         三,經過智能探測,如cpdetector,它是目前口碑最好的java實現的智能探測編碼,是基於統計實現的,因此註定會有必定的錯誤率,通過個人實測,若干特殊網頁,它確實是不許確的,如網頁的meta中charset和實際的瀏覽器識別的正常顯示的charset不相同的狀況,它的識別也是錯誤的,因此最後我堅定沒用它,而用了基於簡單規則的方式,實際測試1000個種子網址證實,沒發現任何亂碼,除了一個站點它自身是亂碼以外。優化

     重點說下亂碼的解決策略:

         1、首先讀取http header中的content_type的charset,若是有,則認定該charset是確定準確的,直接作爲解碼的編碼格式便可。  

         2、再按系統默認編碼即UTF-8,去按行讀取源網頁中的meta和title的值,因爲這兩個值均爲英文標籤,因此在獲取時確定不會受到亂碼的影響,故能夠按UTF-8方式準確獲取charset和title的值,此時的title有多是亂碼。

         3、因爲有很多中文站點中,雖然meta中的charset顯示的是utf-8或是GBK,但實際的瀏覽器解析到的正常編碼正好相反爲gbk或是UTF-8,面對這種特例,而又發現只有在國內的站點會有如此狀況,故作規則以下:

                   (1)首先判斷此時的title若均爲標點、字母、數字、中英文符號、GB18030的中文字符等,則認爲這次的默認編碼就是源網頁的實際編碼,而無論得到的charset是怎樣的,並將charset設成爲系統的默認編碼utf-8。

                   (2)若是title知足第(1)條件,則用獲得的charset去解碼原始的字節流(若是charset就是utf-8,則省略後一步,直接將該charset做爲實際的編碼處理,即utf-8,緣由在於不少俄文、西里爾文的標題可能是UTF-8編碼,但均不屬於中文行列)。並獲取新解析出來的源網頁字符串的title。此時的新解碼的charset即爲最終的源網頁認定的charset。

          解碼完成後,在保存源網頁的實際數據時,先對獲得的原始字節數組按上一步獲得的charset解碼,即:

          String source_webpage_string=new String(original_byte_array,charset);

          此時獲得的source_webpage_string即爲正常的源網頁中,再進行重編碼:

          new_byte_array=source_webpage_string.getBytes(system.defaultEncoding);//即utf-8

          再用utf-8對正常的串進行編碼,獲得統一編碼下的字節數組,經過java io寫入到即定的大文件中便可。

          固然若是charset值就是默認的utf-8,則無需解碼,直接存儲便可。

     有人會問爲什麼要先解碼?答案是:解碼是爲了統一編碼。作爲爬蟲系統,會有來自成千上萬個站點的網頁存儲到系統中,而網頁的編碼有不少,像GBK、Unicode、big五、shift-js、windows-1521等等,若是直接存儲而不統一編碼在應用端讀取的時候,就要讀出字節數組後按原始的編碼解析才能獲得非亂碼顯示,到視圖端顯示的時候也要如此轉化,顯然這樣作是不合理的,而應該是在爬取下來存儲的時候,都統一存成UTF-8編碼的便可,統一以後,不管哪一端來讀,均可以直接按UTF-8來處理。這也是統用、主流作法。

     通過如上的處理,各個國家的站點出現亂碼的機率幾乎爲0,本人的實際測試狀況亦是如此。

     但願對正在學習網絡爬蟲的同窗們有幫助。

相關文章
相關標籤/搜索