Eine gute Möglichkeit, den Zeichensatz / die Codierung einer HTTP-Antwort in Python zu erhalten

Suchen Sie nach einer einfachen Möglichkeit, die Zeichensatz- / Codierungsinformationen einer HTTP-Antwort mithilfe von Python urllib2 oder einer anderen Python-Bibliothek abzurufen.

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

Ich weiß, dass es manchmal im Header “Content-Type” vorhanden ist, aber dieser Header hat andere Informationen und ist in eine Zeichenfolge eingebettet, die ich analysieren müsste. Zum Beispiel ist der Content-Type-Header von Google zurückgegeben

 >>> conn.headers.getheader('content-type') 'text/html; charset=utf-8' 

Ich könnte damit arbeiten, aber ich bin mir nicht sicher, wie das Format konsistent sein wird. Ich bin mir ziemlich sicher, dass es möglich ist, dass Zeichensatz komplett fehlt, also müsste ich mit diesem Randfall fertig werden. Irgendeine Art von String-Split-Operation, um die “utf-8” daraus zu machen, scheint so zu sein, als wäre es der falsche Weg, um so etwas zu tun.

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

Das ist die Art von Code, der sich anfühlt, als würde er zu viel arbeiten. Ich bin mir auch nicht sicher, ob es in jedem Fall funktioniert. Hat jemand einen besseren Weg dies zu tun?

Um HTTP-Header zu analysieren, könnten Sie cgi.parse_header() :

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

Oder das Antwortobjekt verwenden:

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

Im Allgemeinen kann der Server über die Kodierung lügen oder sie überhaupt nicht melden (der Standardwert hängt vom Inhaltstyp ab) oder die Kodierung könnte innerhalb des Antwortkörpers angegeben werden, zB -Element in html-Dokumenten oder in xml-Deklaration für xml Unterlagen. Als letztes Mittel konnte die Codierung aus dem Inhalt selbst erraten werden.

Sie können requests , um Unicode-Text zu erhalten:

 import requests # pip install requests r = requests.get(url) unicode_str = r.text # may use `chardet` to auto-detect encoding 

Oder BeautifulSoup zum Analysieren von HTML (und Konvertieren in Unicode als Nebeneffekt):

 from bs4 import BeautifulSoup # pip install beautifulsoup4 soup = BeautifulSoup(urllib2.urlopen(url)) # may use `cchardet` for speed # ... 

Oder bs4.UnicodeDammit direkt für beliebige Inhalte (nicht unbedingt ein html):

 from bs4 import UnicodeDammit dammit = UnicodeDammit(b"Sacr\xc3\xa9 bleu!") print(dammit.unicode_markup) # -> Sacré bleu! print(dammit.original_encoding) # -> utf-8 

Wenn Sie sich mit dem Flask / Werkzeug- Webentwicklungs-Stack auskennen, werden Sie froh sein, dass die Bibliothek von Werkzeug eine Antwort für genau diese Art von HTTP-Header-Parsing hat und berücksichtigt, dass der Inhaltstyp nicht spezifiziert ist alles, wie du es wolltest.

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

Dann kannst du Folgendes tun:

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

Beachten Sie, dass wenn kein charset wird, stattdessen charset wird:

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

Es funktioniert sogar, wenn Sie nichts als eine leere Zeichenfolge oder ein dict liefern:

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

So scheint es GENAU zu sein, wonach Sie gesucht haben! Wenn Sie sich den Quellcode ansehen, werden Sie feststellen, dass sie Ihren Zweck erfüllt haben: 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. ... 

Hoffe das hilft jemandem eines Tages! 🙂

Die requests macht das einfach:

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

Zeichensätze können auf viele Arten angegeben werden , aber oft in den Kopfzeilen.

 >>> 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() >>> 

Der letzte gab keinen Zeichensatz an, also gab get_content_charset() None .

Um richtig (dh in einer Browser-ähnlichen Weise – wir können nicht besser machen) dekodieren Sie HTML, das Sie berücksichtigen müssen:

  1. Content-Type HTTP-Headerwert;
  2. Stücklistenmarken;
  3. -Tags im Seitenhauptteil;
  4. Unterschiede zwischen den im Web verwendeten Codierungsnamen und den in Python stdlib verfügbaren Codierungsnamen;
  5. Wenn alles andere scheitert, ist als letzte Möglichkeit ein Raten auf Basis von Statistiken eine Option.

All dies ist in der function w3lib.encoding.html_to_unicode implementiert: Es hat die html_to_unicode(content_type_header, html_body_str, default_encoding='utf8', auto_detect_fun=None) und gibt das Tupel (detected_encoding, unicode_html_content) .

Anfragen, BeautifulSoup, UnicodeDamnnit, chardet oder flashs parse_options_header sind nicht die richtigen Lösungen, da sie alle an einigen dieser Punkte scheitern.