2009-09-30 65 views
32

問題

當使用python屏幕抓取網頁時,必須知道頁面的字符編碼。如果你的字符編碼錯誤,你的輸出會被搞亂。如何在python中下載正確字符集的任何(!)網頁?

人們通常會使用一些基本的技術來檢測編碼。他們要麼使用標題中的字符集,要麼使用元標記中定義的字符集,或者使用encoding detector(它不關心元標記或標題)。 通過僅使用一種這些技術,有時您將得不到與在瀏覽器中相同的結果。

瀏覽器做這種方式:

  • Meta標籤始終把標題中定義的優先級(或XML定義)
  • 編碼使用時,有沒有在meta標籤定義的字符集
  • 如果根本沒有定義編碼,而是編碼檢測的時間。

(嗯......至少這是我相信的樣子大多數瀏覽器做到這一點。文檔非常稀少。)

我正在尋找的是能夠確定該字符集的庫瀏覽器的方式。我相信我不是第一個需要適當解決這個問題的人。

解決方案(我還沒有嘗試過...)

Beautiful Soup's documentation

美麗的湯嘗試以下編碼,按優先順序,把你的文件轉換成Unicode:

  • 你傳遞的 fromEncoding參數湯 構造的編碼。
  • 在文檔本身中發現的編碼:例如,在XML聲明中或(對於HTML文檔)一個http-equiv META標籤。如果Beautiful Soup在文檔中發現這種編碼,它會從頭開始再次解析文檔,並嘗試新編碼。唯一的例外是,如果您明確指定了編碼,並且該編碼實際上起作用:那麼它將忽略它在文檔中找到的任何編碼。
  • 通過查看文件的前幾個字節來查看編碼。如果在此階段檢測到編碼 ,則它將是UTF- *編碼的 ,EBCDIC或ASCII之一。
  • 一個 編碼嗅探chardet 庫,如果你有它的安裝。
  • UTF-8
  • 的Windows 1252
+4

您無法下載「任何」頁面,其中包含正確的字符集。當沒有指定正確的字符集時,瀏覽器總是猜錯。我使用FF中的view->編碼菜單來修復每天不正確的猜測。你想做得儘可能好,但放棄正確猜測每一頁。 – 2009-09-30 02:08:12

+7

猜測字符集是邪惡的,首先讓我們陷入這個混亂。如果瀏覽器從未試圖猜測,開發人員將被迫學習HTTP標頭,並始終指定編碼。猜測意味着某個時候你會錯的 – 2009-10-04 01:04:27

+0

gnibbler,猜測是最後的手段 – 2009-10-09 15:46:56

回答

3

我會用html5lib這一點。

+2

這看起來非常好。有關如何進行編碼發現的文檔: http://html5lib.readthedocs。組織/ EN /最新/ movingparts.html#編碼發現 – 2013-12-11 18:53:32

14

使用Universal Encoding Detector

>>> import chardet 
>>> chardet.detect(urlread("http://google.cn/")) 
{'encoding': 'GB2312', 'confidence': 0.99} 

另一種選擇是隻使用wget:

import os 
    h = os.popen('wget -q -O foo1.txt http://foo.html') 
    h.close() 
    s = open('foo1.txt').read() 
+0

這是不好的,因爲它有時會失敗。另請參閱:http://chardet.feedparser.org/docs/faq.html#faq.yippie(Yippie!) – 2009-09-30 00:48:22

+0

這種方法的主要問題是您忽略頁面的明確指定的字符編碼。 – 2009-09-30 00:49:48

+2

好吧,那麼恐怕沒有銀彈 - 所以請自己寫。 :) – rajax 2009-09-30 00:50:37

36

當你下載使用urllib或urllib2文件,您可以瞭解是否傳輸了字符集標題:

fp = urllib2.urlopen(request) 
charset = fp.headers.getparam('charset') 

您可以使用BeautifulSoup來定位HTML meta元素:

soup = BeatifulSoup.BeautifulSoup(data) 
meta = soup.findAll('meta', {'http-equiv':lambda v:v.lower()=='content-type'}) 

如果兩者都不可用,瀏覽器通常回落到用戶配置,具有自動檢測相結合。按照rajax的建議,你可以使用chardet模塊。如果你有可用的用戶配置,告訴你這個頁面應該是中文的(比如說),你可能會做得更好。

+0

我認爲它是'getparam' – u0b34a0f6ae 2009-09-30 12:09:31

+5

@ kaiser.se:right;它是3.x中的'get_param'(但是,它也是urllib.request) – 2009-10-07 18:25:37

+0

不幸的是(至少在Python 2.7中)urllib2不會從Content-Type頭解析出字符集,所以你需要做一些類似答案在http://stackoverflow.com/a/1020931/69707 – 2011-11-29 21:35:24

1

,而不是試圖讓一個頁面,然後找出字符集的瀏覽器會用,爲什麼不直接使用瀏覽器抓取網頁和檢查什麼字符集,它使用..

from win32com.client import DispatchWithEvents 
import threading 


stopEvent=threading.Event() 

class EventHandler(object): 
    def OnDownloadBegin(self): 
     pass 

def waitUntilReady(ie): 
    """ 
    copypasted from 
    http://mail.python.org/pipermail/python-win32/2004-June/002040.html 
    """ 
    if ie.ReadyState!=4: 
     while 1: 
      print "waiting" 
      pythoncom.PumpWaitingMessages() 
      stopEvent.wait(.2) 
      if stopEvent.isSet() or ie.ReadyState==4: 
       stopEvent.clear() 
       break; 

ie = DispatchWithEvents("InternetExplorer.Application", EventHandler) 
ie.Visible = 0 
ie.Navigate('http://kskky.info') 
waitUntilReady(ie) 
d = ie.Document 
print d.CharSet 
+0

剛剛在origo.hu上測試過,儘管速度很慢 - 可能試着用firefox activex組件代替 – Ravi 2009-09-30 18:45:22

3

好像你需要提出的答案的混合模式:

  1. 使用的urllib
  2. 抓取頁面用美麗的湯或其他方法查找<meta>標籤
  3. 如果不存在元標記,請檢查由urllib返回的標頭
  4. 如果仍然沒有給出答案,請使用通用編碼檢測器。

我真的不相信你會找到比這更好的東西。

事實上,如果您進一步閱讀了關於其他答案的評論中鏈接到的常見問題解答,那麼檢測器庫的作者就是這樣認爲的。

如果您認爲常見問題解答是瀏覽器所做的(正如您原來的問題所要求的那樣),因爲探測器是Firefox探測代碼的一個端口。

+0

我覺得奇怪的是,沒有現成的庫/代碼片段。 – 2009-10-09 19:27:45

+0

Stobor指出feedparser.py(不幸的是僅用於XML)的存在,但包含了我需要的大部分內容。 – 2009-10-09 19:35:45

+0

該算法不正確,因爲HTTP標頭應該優先於元標籤。它也沒有BOM標記和編碼標準化步驟(HTML/HTTP中的編碼名稱與Python提供的名稱不同)。 – 2017-05-17 10:27:11

2

與requests.get(url).text或urlopen不同,Scrapy會下載一個頁面,並檢測它的正確編碼。要做到這一點,它會嘗試遵循類似瀏覽器的規則 - 這是最好的做法,因爲網站所有者有動力使他們的網站在瀏覽器中工作。 Scrapy需要採用HTTP標頭,<meta>標籤,BOM標記以及帳戶中編碼名稱的差異。

基於內容的猜測(chardet,UnicodeDammit)本身並不是一個正確的解決方案,因爲它可能會失敗;當標題或<meta>或BOM標記不可用或不提供任何信息時,它應該僅用作最後的手段。

您不必使用Scrapy來獲取其編碼檢測功能;他們被釋放(與其他一些東西)在一個名爲w3lib的獨立庫:https://github.com/scrapy/w3lib

要獲得頁面的編碼和Unicode體使用w3lib.encoding.html_to_unicode功能,具有後備猜測基於內容:

import chardet 
from w3lib.encoding import html_to_unicode 

def _guess_encoding(data): 
    return chardet.detect(data).get('encoding') 

detected_encoding, html_content_unicode = html_to_unicode(
    content_type_header, 
    html_content_bytes, 
    default_encoding='utf8', 
    auto_detect_fun=_guess_encoding, 
) 
相關問題