拉丁语编码问题

时间:2017-04-26 14:12:50

标签: python encoding web-scraping beautifulsoup lxml

我正在使用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

1 个答案:

答案 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字符检测代码的成功率;支持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-types,HTTP指定响应应该被视为使用Latin-1,requests也会遵循,但对于大多数HTML数据,默认值都是不正确的。