手把手教你寫網絡爬蟲(8)html
做者:拓海 (https://github.com/tuohai666)java
摘要:從零開始寫爬蟲,初學者的速成指南!git
封面:github
字符編解碼是爬蟲裏必學的一項知識,在咱們的爬蟲生涯中遲早會爬到亂碼的網頁,與其遇到時惶恐不安,不如早學早好,完全避免亂碼問題。算法
什麼是字符集數據庫
在介紹字符編碼以前,咱們先了解下什麼是字符集。瀏覽器
字符(Character)是各類文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。字符集(Character set)是多個字符的集合,字符集種類較多,每一個字符集包含的字符個數不一樣,常見字符集:ASCII字符集、GBK字符集、Unicode字符集等。網絡
什麼是字符編碼app
字符編碼和字符集不一樣。字符集只是字符的集合,沒法進行網絡傳送、處理,必須經編碼後才能使用。如Unicode字符集可依不一樣需求以UTF-八、UTF-1六、UTF-32等方式編碼。less
字符編碼就是以二進制的數字來對應字符集的字符。各個國家和地區在制定編碼標準的時候,「字符的集合」和「編碼」通常都是同時制定的。所以,日常咱們所說的「字符集」,除了有「字符的集合」這層含義外,同時也包含了「編碼」的含義。
經常使用字符集
簡單介紹幾個常見的。
ASCII:
ASCII是學計算機同窗的啓蒙字符集,通常是從這本書裏學到的:
請容許我懷舊一下,如下引用譚浩強老師的講解:
中文字符集:
GB2312:包含6763個漢字。
GBK:包含21003個漢字。GBK兼容GB2312,也就是說用GB2312編碼的漢字能夠用GBK來解碼。
GB18030:收錄了70000個漢字,這麼可能是由於包含了少數民族文字。一樣兼容GBK和GB2312。
Unicode:Unicode 是爲了解決傳統的字符編碼方案的侷限而產生的,它爲每種語言中的每一個字符設定了統一而且惟一的二進制編碼,以知足跨語言、跨平臺進行文本轉換、處理的要求。具備多種編碼方式,如UTF-七、 UTF-八、UTF-1六、UTF-32等。
簡單的說亂碼的出現是由於:編碼和解碼時用了不一樣的字符集。對應到真實生活中,就比如是一個英國人爲了表示祝福在紙上寫了bless(編碼)。而一個法國人拿到了這張紙,因爲在法語中bless表示受傷的意思,因此認爲他想表達的是受傷(解碼)。同理,在計算機中,一個用UTF-8編碼後的字符,用GBK去解碼。因爲兩個字符集的字庫表不同,同一個漢字在兩個字符表的位置也不一樣,最終就會出現亂碼。
那麼,爬蟲中的亂碼是怎麼產生的,又該如何解決呢?
假設咱們的爬蟲是java開發的,網絡請求庫使用OkHttp,網頁存儲到MongoDB中。亂碼產生的過程以下:
顯然,致使亂碼的根本緣由就是OkHttp在最初使用了錯誤的解碼方式進行解碼。因此要解決這個問題,就要讓OkHttp知道網頁的編碼類型,進行正確的解碼。
網頁有兩種約定的方式告訴爬蟲本身使用的是什麼編碼方式:
1. Http協議的響應頭中的約定:
Content-Type: text/html;charset=utf-8
2. Html中meta標籤中的約定:
<meta http-equiv=」Content-Type」 content=」text/html; charset=UTF-8「/>
從約定中獲取網頁的編碼後,Okhttp就能夠正確的解碼了。然而實際狀況卻並不樂觀,不少網頁並不遵照約定,缺乏這兩個信息。有人經過Alexa統計各國遵照這個約定的網頁數:
語言 |
URL後綴 |
URL數 |
HTTP頭中包含 charset的URL數 |
Chinese |
.cn |
10086 |
3776 |
English |
.us/.uk |
21565 |
13223 |
Russian |
.ru |
39453 |
28257 |
Japanese |
.jp |
20339 |
6833 |
Arabic |
.iq |
1904 |
1093 |
German |
.de |
35318 |
23225 |
Persian |
.ir |
7396 |
4018 |
Indian |
.in |
12236 |
4867 |
Total |
all |
148297 |
85292 |
結果代表咱們不能被動的依賴網頁告訴咱們,而要根據網頁內容來主動探測其編碼類型。
什麼是字符編碼自動檢測?
它是指當面對一串不知道編碼信息的字節流的時候,嘗試着肯定一種編碼方式以使咱們可以讀懂其中的文本內容。它就像咱們沒有解密鑰匙的時候,嘗試破解出編碼。
那不是不可能的嗎?
一般來講,是的,不可能。可是,有一些編碼方式爲特定的語言作了優化,而語言並不是隨機存在的。有一些字符序列在某種語言中老是會出現,而其餘一些序列對該語言來講則毫無心義。一個熟練掌握英語的人翻開報紙,而後發現「txzqJv 2!dasd0a QqdKjvz」這樣一些序列,他會立刻意識到這不是英語(即便它徹底由英語中的字母組成)。經過研究許多具備「表明性(typical)」的文本,計算機算法能夠模擬人的這種對語言的感知,而且對一段文本的語言作出啓發性的猜想。換句話說就是,檢測編碼信息就是檢測語言的類型,並輔之一些額外信息,好比每種語言一般會使用哪些編碼方式。
這樣的算法存在嗎?
結果證實,是的,它存在。全部主流的瀏覽器都有字符編碼自動檢測的功能,由於互聯網上老是充斥着大量缺少編碼信息的頁面。Mozilla Firefox包含有一個自動檢測字符編碼的庫,已經移植到Python中,叫作chardet。
chardet使用
安裝:
pip install chardet
使用:
>>> import urllib
>>> rawdata = urllib.urlopen('http://www.jd.com/').read()
>>> import chardet
>>> chardet.detect(rawdata)
{'confidence': 0.98999999999999999, 'language': '', 'encoding': 'utf-8'}
注意:返回結果中有confidence,即置信度,這說明探測結果不是100%準確的。
使用其餘語言的小夥伴不用擔憂,chardet在不少語言中都有移植版。不過C++好像沒有太好的選擇,能夠考慮使用IBM的ICU(http://site.icu-project.org/)。
《A composite approach to language/encoding detection》
(https://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html)
這篇論文解釋了Chardet背後使用的探測算法,分別是「編碼模式方法」、「字符分佈方法」和「雙字符序列分佈方法」。最後說明了三種方法組合使用的必要性,並舉例說明如何組合使用。
《Charset Encoding Detection of HTML Documents A Practical Experience》
利用現有的探測技術,經過一些技巧來提升探測的準確性。主要原理是組合使用Mozilla CharDet和IBM ICU,並在探測前巧妙的去掉了HTML標籤。雖然這是伊朗大學發的Paper,但聽說這種方法已經在生產環境取得了很好的效果,目前正應用在一個10億級別數據量的大型爬蟲上。
最近聊的話題愈來愈沉重,想必你們也累了。下期打算帶你們一塊兒放鬆一下,聊點輕鬆的話題。從系列的開篇到如今也有半年了,技術領域有了不小的更新,出現了一些好用的工具,咱們須要替換哪些工具呢?請聽下回分解!