2017-04-26 97 views
0

我正在使用python web scraper從this webpage中提取數據。它包含像ą,č,è,ė,į,š,ø,ū,ž這樣的拉丁字符。我使用BeautifulSoup來識別編碼:拉丁文編碼問題

def decode_html(html_string): 
    converted = UnicodeDammit(html_string) 
    print(converted.original_encoding) 
    if not converted.unicode_markup: 
     raise UnicodeDecodeError(
      "Failed to detect encoding, tried [%s]", 
      ', '.join(converted.tried_encodings)) 
    return converted.unicode_markup 

它似乎總是使用的編碼是「windows-1252」。但是,當打印到文件或控制檯時,這會將字符如?變成ë和ø。我使用lxml庫來刮取數據。所以我認爲它使用了錯誤的編碼,但奇怪的是,如果我使用lxml.html.open_in_browser(decoded_html),所有字符都恢復正常。如何在沒有所有mojibake的情況下將文字打印到文件中?

這是我在用的輸出:

def write(filename, obj): 
with open(filename, "w", encoding="utf-8") as output: 
    json.dump(obj, output, cls=CustomEncoder, ensure_ascii=False) 
return 

回答

2

從具體的網頁設置HTTP頭文件中試圖加載:

Content-Type:text/html; charset=windows-1257 

所以Windows 1252會導致無效結果。 BeautifulSoup猜測(基於統計模型),並猜測錯誤。正如您所注意到,使用1252,而不是導致不正確的代碼點:

>>> 'ė'.encode('cp1257').decode('cp1252') 
'ë' 
>>> 'ų'.encode('cp1257').decode('cp1252') 
'ø' 

CP1252是在BeautifulSoup基本字符集檢測執行回退。您可以通過安裝外部庫來提高BeautifulSoup字符檢測代碼的成功率;支持chardetcchardet。這兩個庫分別猜測MacCyrillic和ISO-8859-13(都是錯誤的,但cchardet非常接近,可能足夠接近)。

在這種特殊情況下,您可以改爲使用HTTP標頭。在請求時,我一般使用:

import requests 
from bs4 import BeautifulSoup 
from bs4.dammit import EncodingDetector 

resp = requests.get(url) 
http_encoding = resp.encoding if 'charset' in resp.headers.get('content-type', '').lower() else None 
html_encoding = EncodingDetector.find_declared_encoding(resp.content, is_html=True) 
encoding = html_encoding or http_encoding 
soup = BeautifulSoup(resp.content, 'lxml', from_encoding=encoding) 

上面只使用編碼從響應如果明確地由服務器設置,並且沒有HTML <meta>報頭。對於text/* mime類型,HTTP指定應將響應視爲使用拉丁語-1,即requests也遵守,但對於大多數HTML數據,該默認值將不正確。

+0

這是不是有一個不同的潛在問題,它會始終用字符集標題中提供的編碼重寫文檔中指定的編碼? – pvg

+0

@pvg:是的,如果服務器明確設置了編碼*和* HTML文檔設置了一個(通過meta標頭),那麼在這種情況下,服務器內容類型會勝出。我將用一個首先查找HTML聲明的解決方案進行更新。 –