03-網頁內容的編碼檢測
鄭昀 201005 隸屬於《02.資料解析》小節
我們需要確定網頁的內容/標題等文字的編碼格式,比如 utf-8 、gb2312 等。
通用檢測順序
一般來說,國內外類似服務的檢測順序是(參見參考資源A):
- charset parameter in HTTP
Content-type
header.
<meta http-equiv="content-type">
element in the <head>
of a web page for HTML documents.
- encoding attribute in the XML prolog for XML documents.
- Auto-detect the character encoding as a last resort.
也就是,HTTP Response 的 header 裡 content-type 如果指定 charset 的話,優先順序是高於 HTML 裡的 content-type 的。
由於我國網路服務商不一定保持 HTTP Content-type
header 與 meta charset 一致,比如新浪新聞、和訊、網易新聞的 html 裡都會寫明 meta charset 是 gb2312,但新浪新聞的 HTTP Content-type
header 裡卻只輸出:Content-Type: text/html ,並沒有給出 charset 參數。網易新聞則 HTTP Header 中指定 GBK ,而 HTML 裡卻指定 GB2312 。
國外的一些服務探測我國網站時,容易因此得到亂碼,如我的文章《Yahoo! Pipe的charset問題之解決方案》所說的。
這樣帶來的一個問題就是:
當 HTTP Content-type header 與 meta charset 不一致時,到底採信誰的聲明?
當然也可以用 chardet 來檢測內容,但 chardet 非常消耗資源,在網路爬蟲中頻繁調用 chardet 吞吐大量 html 字串,會降低抓取效率。
BeautifulSoup 自動探測機制
BeautifulSoup 會自動判斷頁面編碼,如果判斷不出來就調用 chardet 探測。它的探測順序是:
Beautiful Soup tries the following encodings, in order of priority, to turn your document into Unicode:
- An encoding you pass in as the
fromEncoding
argument to the soup constructor.
- An encoding discovered in the document itself: for instance, in an XML declaration or (for HTML documents) an
http-equiv
META tag. If Beautiful Soup finds this kind of encoding within the document, it parses the document again from the beginning and gives the new encoding a try. The only exception is if you explicitly specified an encoding, and that encoding actually worked: then it will ignore any encoding it finds in the document.
- An encoding sniffed by looking at the first few bytes of the file. If an encoding is detected at this stage, it will be one of the UTF-* encodings, EBCDIC, or ASCII.
- An encoding sniffed by the
chardet
library, if you have it installed.
- UTF-8
- Windows-1252
BeautifulSoup 優先用 meta charset 指示的編碼進行探測,但未必對。
舉一個異常的例子,http://www.miniclip.com/games/cn/ ,它的 HTTP Content-type header 是 utf-8,而 meta charset 卻是 iso-8859-1,實際上它的編碼是 utf-8 。
對於這種情況,怎麼辦?
可以讓 BeautifulSoup 強制按照 HTTP Content-type 聲明的編碼做轉換:
from BeautifulSoup import BeautifulSoup
from urllib import urlopen
response=urlopen('http://www.miniclip.com/games/cn/')
charset=BeautifulSoup.CHARSET_RE.search(response.headers['content-type'])
charset=charset and charset.group(3) or None
page=BeautifulSoup(response.read(),fromEncoding=charset)
參考資源:
A、 Encoding detection library in python ;
B、 [CPyUG:76548] 關於正確的判斷頁面編碼 ;
C、 Beautifulsoup 分析網頁亂碼問題解決辦法 ;
D、 Python中文問題研究 ;
E、 BeautifulSoup處理gb2312編碼網頁的一個bug ;