我正在使用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
答案 0 :(得分:2)
在您尝试加载的特定网页上设置的HTTP标头中:
Content-Type:text/html; charset=windows-1257
因此Windows-1252会导致无效结果。 BeautifulSoup做了一个猜测(基于统计模型),并猜测错了。正如您所注意到的,使用1252会导致错误的代码点:
>>> 'ė'.encode('cp1257').decode('cp1252')
'ë'
>>> 'ų'.encode('cp1257').decode('cp1252')
'ø'
CP1252是BeautifulSoup中基本字符集检测实现的后备。您可以通过安装外部库来提高BeautifulSoup字符检测代码的成功率;支持chardet
和cchardet
。这两个库分别猜测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-types,HTTP指定响应应该被视为使用Latin-1,requests
也会遵循,但对于大多数HTML数据,默认值都是不正确的。