web-dev-qa-db-ja.com

PythonでHTTP応答の文字セット/エンコードを取得する良い方法

Python urllib2、または他のPythonライブラリを使用して、HTTP応答の文字セット/エンコード情報を取得する簡単な方法を探しています。

>>> url = 'http://some.url.value'
>>> request = urllib2.Request(url)
>>> conn = urllib2.urlopen(request)
>>> response_encoding = ?

'Content-Type'ヘッダーに存在することもありますが、そのヘッダーには他の情報があり、解析する必要がある文字列に埋め込まれています。たとえば、Googleが返すContent-Typeヘッダーは

>>> 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]

それは、あまりにも多くの作業をしているように感じられる種類のコードです。また、すべてのケースで機能するかどうかもわかりません。誰かがこれを行うためのより良い方法を持っていますか?

26
Clay Wardell

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)

一般に、サーバーはエンコーディングについて嘘をついたり、まったく報告しない(デフォルトはコンテンツタイプに依存する)か、エンコーディングは応答本文内で指定される場合があります。たとえば、htmlドキュメントまたはxml宣言の<meta>要素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
22
jfs

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'})

そのため、次のことができます。

 >>> content_type_header[1].get('charset')
 'utf-8'

charsetが指定されていない場合、代わりに以下が生成されることに注意してください。

 >>> parse_options_header('text/html')
 ('text/html', {})

空の文字列または辞書以外を指定しない場合でも機能します:

 >>> parse_options_header({})
 ('', {})
 >>> parse_options_header('')
 ('', {})

したがって、まさにあなたが探していたもののようです!ソースコードを見ると、目的を念頭に置いていることがわかります。 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.
    ...

これがいつか誰かを助けることを願っています! :)

7
Brian Peterson

requests ライブラリはこれを簡単にします:

>>> import requests
>>> r = requests.get('http://some.url.value')
>>> r.encoding
'utf-8' # e.g.
5
dnozay

文字セットは 多くの方法 で指定できますが、ヘッダーで頻繁に指定されます。

_>>> 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を返しました。

3
Cees Timmerman

適切に(つまり、ブラウザのような方法で-改善することはできません)HTMLをデコードするには、考慮する必要があるHTML:

  1. Content-Type HTTPヘッダー値。
  2. BOMマーク;
  3. ページ本文の_<meta>_タグ。
  4. Webで使用される定義されたエンコーディング名とPython stdlib;
  5. 最後の手段として、他のすべてが失敗した場合、統計に基づいた推測がオプションです。

上記のすべては w3lib.encoding.html_to_unicode functionで実装されています。これはhtml_to_unicode(content_type_header, html_body_str, default_encoding='utf8', auto_detect_fun=None)署名を持ち、_(detected_encoding, unicode_html_content)_ Tupleを返します。

リクエスト、BeautifulSoup、UnicodeDamnnit、chardet、flaskのparse_options_headerは、これらのいくつかの点ですべて失敗するため、正しいソリューションではありません。

1
Mikhail Korobov

これは私にとって完璧に機能するものです。私はpython 2.7および3.4​​を使用しています

print (text.encode('cp850','replace'))
0
Usama Tahir