Requests 库 ISO-8859-1 编码问题

Requests.Response 的 encoding 保存了网页的编码类型,如:

In [5]: resp = requests.get('http://www.baidu.com')

In [6]: resp.encoding
Out[6]: 'utf-8'

值取自于服务器的 HTTP 响应头中 Content-type 字段的 charset:

Content-Type:text/html; charset=utf-8

服务端不会返回 charset 的情况下默认为 ISO-8859-1。为了识别出页面的编码,还可以考虑 HTML 中的 charset,以下为我写的一个提取函数:

def get_charset_from_html(html):
    """从 HTML 中提取编码类型
    """
    import re
    search = (re.search(r'charset=["\']([^"]+)["\']', html) or
              re.search('charset=\w+', html))

    if not search:
        return None

    search_result = search.group()
    if search_result.find('=') > -1:
        return search_result.split('=')[1] \
                            .strip().replace("'", "") \
                            .replace('"', "")

    return None


def test_get_charset_from_html():
    assert(get_charset_from_html("<meta charset='gbk2312'/>") == "gbk2312")
    assert(get_charset_from_html("<meta charset=\"gbk2312\"/>") == "gbk2312")
    assert(get_charset_from_html("<meta charset=gbk2312/>") == "gbk2312")
    assert(get_charset_from_html("content='text/html; charset=GBK'>") == "GBK")

所以,可以这样相对较好的方法取 HTML:

def url_to_html(url, headers=default_headers, timeout=10):
    req = requests.get(url, timeout=timeout, headers=headers)
    # requests 未能正确识别的编码就默认返回 ISO-885901
    # 所以尝试从 HTML 中找定义的编码
    if req.text and req.encoding == 'ISO-8859-1':
        encoding = get_charset_from_html(req.text)
        # XXX 如果页面也没定义编码?遇到再说吧
        if encoding:
            req.encoding = encoding

    return req.text