Хороший способ получить кодировку / кодировку ответа HTTP в Python
Ищете простой способ получить информацию о кодировке / кодировке HTTP-ответа, используя Python urllib2 или любую другую библиотеку Python.
>>> url = 'http://some.url.value'
>>> request = urllib2.Request(url)
>>> conn = urllib2.urlopen(request)
>>> response_encoding = ?
Я знаю, что он иногда присутствует в заголовке Content-Type, но этот заголовок содержит другую информацию, и он встроен в строку, которую мне нужно будет проанализировать. Например, заголовок Content-Type, возвращаемый Google:
>>> conn.headers.getheader('content-type')
'text/html; charset=utf-8'
Я мог бы работать с этим, но я не уверен, насколько последовательным будет формат. Я почти уверен, что кодировка может отсутствовать полностью, поэтому мне придется заняться этим крайним случаем. Похоже, что-то вроде операции разбиения строки, чтобы вытащить из нее 'utf-8', должно быть неправильным способом.
>>> content_type_header = conn.headers.getheader('content-type')
>>> if '=' in content_type_header:
>>> charset = content_type_header.split('=')[1]
Это тот код, который кажется, что он делает слишком много работы. Я также не уверен, будет ли это работать в каждом случае. У кого-нибудь есть лучший способ сделать это?
5 ответов
Для разбора http заголовка вы можете использовать cgi.parse_header()
:
_, params = cgi.parse_header('text/html; charset=utf-8')
print params['charset'] # -> utf-8
Или используя объект ответа:
response = urllib2.urlopen('http://example.com')
response_encoding = response.headers.getparam('charset')
# or in Python 3: response.headers.get_content_charset(default)
Обычно сервер может лгать о кодировке или вообще не сообщать об этом (значение по умолчанию зависит от типа содержимого), или кодировка может быть указана внутри тела ответа, например: <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
Или же BeautifulSoup
проанализировать HTML (и преобразовать в Unicode как побочный эффект):
from bs4 import BeautifulSoup # pip install beautifulsoup4
soup = BeautifulSoup(urllib2.urlopen(url)) # may use `cchardet` for speed
# ...
Или же 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
Если вы знакомы со стеком веб-разработки Flask/ Werkzeug, вы будете рады узнать, что в библиотеке 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'})
Итак, вы можете сделать:
>>> content_type_header[1].get('charset')
'utf-8'
Обратите внимание, что если charset
не поставляется, вместо этого получится:
>>> parse_options_header('text/html')
('text/html', {})
Это даже работает, если вы не предоставляете ничего, кроме пустой строки или dict:
>>> parse_options_header({})
('', {})
>>> parse_options_header('')
('', {})
Таким образом, кажется, что именно то, что вы искали! Если вы посмотрите на исходный код, вы увидите, что они имели в виду вашу цель: https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/http.py
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.
...
Надеюсь, это поможет кому-нибудь когда-нибудь!:)
requests
библиотека делает это легко:
>>> import requests
>>> r = requests.get('http://some.url.value')
>>> r.encoding
'utf-8' # e.g.
Charsets можно указывать разными способами, но часто это делается в заголовках.
>>> urlopen('http://www.python.org/').info().get_content_charset()
'utf-8'
>>> urlopen('http://www.google.com/').info().get_content_charset()
'iso-8859-1'
>>> urlopen('http://www.python.com/').info().get_content_charset()
>>>
Последний нигде не указывал кодировку, поэтому get_content_charset()
возвращенный None
,
Чтобы правильно (т. Е. В стиле браузера - мы не можем сделать лучше) декодировать HTML, вы должны принять во внимание:
- Значение заголовка HTTP Content-Type;
- Метки спецификации;
<meta>
теги в теле страницы;- Различия между определенными именами кодирования, используемыми в web, и именами кодировки, доступными в Python stdlib;
- В крайнем случае, если все остальное не удается, вариант угадать на основе статистики.
Все вышеперечисленное реализовано в функции w3lib.encoding.html_to_unicode: html_to_unicode(content_type_header, html_body_str, default_encoding='utf8', auto_detect_fun=None)
подпись и возврат (detected_encoding, unicode_html_content)
кортеж.
запросы, BeautifulSoup, UnicodeDamnnit, chardet или parse_options_header фляги не являются правильными решениями, поскольку все они терпят неудачу в некоторых из этих точек.
Это то, что у меня отлично работает. Я использую Python 2.7 и 3.4
print (text.encode('cp850','replace'))