在Python中获取HTTP响应的字符集/编码的好方法

Cla*_*ell 26 python urllib2 httprequest character-encoding

寻找一种使用Python urllib2或任何其他Python库获取HTTP响应的字符集/编码信息的简单方法.

>>> url = 'http://some.url.value'
>>> request = urllib2.Request(url)
>>> conn = urllib2.urlopen(request)
>>> response_encoding = ?
Run Code Online (Sandbox Code Playgroud)

我知道它有时出现在'Content-Type'标题中,但该标题有其他信息,并且它嵌入在我需要解析的字符串中.例如,Google返回的Content-Type标头是

>>> conn.headers.getheader('content-type')
'text/html; charset=utf-8'
Run Code Online (Sandbox Code Playgroud)

我可以使用它,但我不确定格式的一致性.我很确定charset可能完全丢失,所以我必须处理这个边缘情况.某种类型的字符串拆分操作使得"utf-8"从中看出来似乎是做错这种事情的错误方法.

>>> content_type_header = conn.headers.getheader('content-type')
>>> if '=' in content_type_header:
>>>  charset = content_type_header.split('=')[1]
Run Code Online (Sandbox Code Playgroud)

这种代码感觉就像做了太多的工作.我也不确定它是否适用于所有情况.有没有人有更好的方法来做到这一点?

jfs*_*jfs 22

要解析http标头,您可以使用cgi.parse_header():

_, params = cgi.parse_header('text/html; charset=utf-8')
print params['charset'] # -> utf-8
Run Code Online (Sandbox Code Playgroud)

或者使用响应对象:

response = urllib2.urlopen('http://example.com')
response_encoding = response.headers.getparam('charset')
# or in Python 3: response.headers.get_content_charset(default)
Run Code Online (Sandbox Code Playgroud)

通常,服务器可能会对编码撒谎或根本不报告(默认情况下取决于内容类型),或者可能在响应主体内部指定编码,例如,<meta>html文档中的元素或xml文档的xml声明中.作为最后的手段,可以从内容本身猜测编码.

您可以使用requests获取Unicode文本:

import requests # pip install requests

r = requests.get(url)
unicode_str = r.text # may use `chardet` to auto-detect encoding
Run Code Online (Sandbox Code Playgroud)

或者BeautifulSoup解析html(并转换为Unicode作为副作用):

from bs4 import BeautifulSoup # pip install beautifulsoup4

soup = BeautifulSoup(urllib2.urlopen(url)) # may use `cchardet` for speed
# ...
Run Code Online (Sandbox Code Playgroud)

或者bs4.UnicodeDammit直接用于任意内容(不一定是html):

from bs4 import UnicodeDammit

dammit = UnicodeDammit(b"Sacr\xc3\xa9 bleu!")
print(dammit.unicode_markup)
# -> Sacré bleu!
print(dammit.original_encoding)
# -> utf-8
Run Code Online (Sandbox Code Playgroud)


Bri*_*son 7

如果您恰好熟悉Flask/Werkzeug Web开发堆栈,您将很高兴知道Werkzeug库对这种HTTP头解析有一个答案,并考虑到未指定内容类型的情况.所有,就像你想要的一样.

 >>> from werkzeug.http import parse_options_header
 >>> import requests
 >>> url = 'http://some.url.value'
 >>> resp = requests.get(url)
 >>> if resp.status_code is requests.codes.ok:
 ...     content_type_header = resp.headers.get('content_type')
 ...     print content_type_header
 'text/html; charset=utf-8'
 >>> parse_options_header(content_type_header) 
 ('text/html', {'charset': 'utf-8'})
Run Code Online (Sandbox Code Playgroud)

那么你可以这样做:

 >>> content_type_header[1].get('charset')
 'utf-8'
Run Code Online (Sandbox Code Playgroud)

请注意,如果charset未提供,则会生成:

 >>> parse_options_header('text/html')
 ('text/html', {})
Run Code Online (Sandbox Code Playgroud)

它甚至可以工作,如果你不提供任何东西,但空字符串或字典:

 >>> parse_options_header({})
 ('', {})
 >>> parse_options_header('')
 ('', {})
Run Code Online (Sandbox Code Playgroud)

因此,它似乎是你正在寻找的!如果你看一下源代码,你就会发现它们有你的目的:https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/http.py#L320-329

def parse_options_header(value):
    """Parse a ``Content-Type`` like header into a tuple with the content
    type and the options:
    >>> parse_options_header('text/html; charset=utf8')
    ('text/html', {'charset': 'utf8'})
    This should not be used to parse ``Cache-Control`` like headers that use
    a slightly different format.  For these headers use the
    :func:`parse_dict_header` function.
    ...
Run Code Online (Sandbox Code Playgroud)

希望有一天能帮助别人!:)


dno*_*zay 5

requests库使这变得容易:

>>> import requests
>>> r = requests.get('http://some.url.value')
>>> r.encoding
'utf-8' # e.g.
Run Code Online (Sandbox Code Playgroud)

  • 除了请求中的编码检测不正确(未考虑元标记)之外,他们也不愿意解决该问题(https://github.com/kennethreitz/requests/issues/1087)。 (3认同)