關於爬蟲亂碼有很多群友的各式各樣的問題,下邊簡單總結下關於網絡爬蟲的亂碼處理。注意,這里不僅是中文亂碼,還包括一些如日文、韓文 、俄文、藏文之類的亂碼處理,因為他們的解決方式 是一致的,故在此統一說明。
網絡爬蟲,有兩種選擇,一是選擇nutch、hetriex,二是自寫爬蟲,兩者在處理亂碼時,原理是一致的,但前者處理亂碼時,要看懂源碼后進行修改才可以,所以要廢勁一些;而后者更自由方便,可以在編碼處理時進行處理。這也是很多人在用框架寫爬蟲會出現各種各樣的亂碼時,無從下手的原因了,像比較成熟的nutch在處理亂碼時也是比較簡單的,所以依然會出現亂碼,所以需要二次開發才能真正解決亂碼問題。
1、網絡爬蟲出現亂碼的原因
源網頁編碼和爬取下來后的編碼轉換不一致。如源網頁為gbk編碼的字節流,而我們抓取下后程序直接使用utf-8進行編碼並輸出到存儲文件中,這必然會引起亂碼,即當源網頁編碼和抓取下來后程序直接使用處理編碼一致時,則不會出現亂碼,此時再進行統一的字符編碼也就不會出現亂碼了。注意區分源網編碼A、程序直接使用的編碼B、統一轉換字符的編碼C。
A、就是web page的服務器端編碼
B、抓取到的數據,原始情況為字節數組,它是由A來編碼的,只有B=A時,才可以保證不出現亂碼,否則當字符集不兼容時,總是會出現亂碼,此步驟往往用於測試。
C、統一轉碼是指得到網頁的原始編碼A后,再進行的統一編碼,主要是為了將各個網頁的數據統一成一類編碼,往往選擇字符集較大的utf-8為宜。
每個網頁都有自己的編碼,像gbk、utf-8、iso8859-1,以及日文的jp系統編碼、西歐、俄文等編碼各不相同,當進行漫爬時總是會擴展出各種編碼,有的爬蟲是對web網頁進行簡單的編碼識別再進行統一編碼,有的是不做源網頁的判斷直接統一按utf-8來處理,這顯然是會造成亂碼情況。
2、亂碼的解決方法
根據原因來找解決方法,就非常簡單了。
(1) 確定源網頁的編碼A
編碼A往往在網頁中的三個位置,http header的content、網頁的meta charset中、網頁頭中Document定義中。在獲取源網頁編碼時,依次判斷下這三部分數據即可,從前往后,優先級亦是如此。
理論上這樣做是對的,但國內一些網站確是很不符合規范,比如寫的gbk,實際是utf-8,有的是寫的utf-8,但實際是gbk,當然這是很少的一批網站,但確實存在。所以在確定網頁編碼時,應該對該特殊情況做特別處理,如中文檢查、默認編碼等策略。
還有一種情況,是以上三者中均沒有編碼信息,則一般采用cpdetector等第三方網頁編碼智能識別工具來做,其原理即為統計字節數組的特征來概率計算得出實際編碼,有一定的准確率,但我實際的時候發現,其准確率還是很有限的。
但綜合上述的三種編碼確認方式后,幾乎可以完全解決中文亂碼問題,在我基於nutch1.6二次開發的網絡爬蟲系統中,編碼正確經統計可以達到99.99%,也證明了上述方法策略的可行性。
(2)程序通過編碼B對源網頁數據還原
顯然,這里的B是要和A相等的,在java中,如得到的源網頁的字節數組為source_byte_array,那么經過轉換為String str=new String(source_byte_array,B);即在內存上這些字節數組對應的字符是正確編碼和可顯示的,此時的打印輸出結果是正常的,此步驟往往用於debug或是控制台輸出做測試。
(3) 統一轉碼
網絡爬蟲系統數據來源很多,不可能使用數據時,再轉化為其原始的數據,假使這樣做是很廢事的。所以一般的爬蟲系統都要對抓取下來的結果進行統一編碼,從而在使用時做到一致對外,方便使用。此時即是在(2)的基礎上,做一個統一的編碼轉換即可,在java中的實現如下
源網頁的字節數組為source_byte_array
轉換為正常的字符串: String normal_source_str=new String(source_byte_array,C),此時可以用java api直接存儲,但往往不直接寫入字符串,因為一般的爬蟲存儲都是多個源網頁存儲到一個文件中,所以要記錄字節偏移量,故下一步。
再將得到的str轉換為統一的編碼C格式的字節數組,則byte[] new_byte_array=normal_source_str.getBytes(C)即可,此時即可用java io api將數組寫入文件,並記錄相應的字節數組偏移量等,待真正使用時,直接io讀取即可。