Requests: Response.text gibt falsch dekodierten Text zurück (Anfragen 1.2.3, Python 2.7)

Erstellt am 15. Sept. 2013  ·  24Kommentare  ·  Quelle: psf/requests

Wenn der HTTP-Server Inhaltstyp: text/* ohne Kodierung zurückgibt, dekodiert Response.text ihn immer als 'ISO-8859-1'-Text.

Es mag in RFC2616/3.7.1 gültig sein, aber im wirklichen Leben im Jahr 2013 ist dies falsch.

Ich habe eine Beispielseite mit chinesischem Text erstellt:
http://lavr.github.io/python-emails/tests/requests/some-utf8-text.html
Alle Browser rendern diese Seite richtig.
Aber reguests.get gibt ungültigen Text zurück.

Und hier ist ein einfacher Test mit dieser URL:
https://gist.github.com/lavr/6572927

Hilfreichster Kommentar

@lavr (/cc @sigmavirus24), noch einfacher können Sie die Codierung einfach selbst bereitstellen.

>>> r = requests.get('http://irresponsible-server/')
>>> r.encoding = 'utf-8'

Fahren Sie dann normal fort.

Alle 24 Kommentare

Danke dafür @lavr!

Dies ist eine bewusste Designentscheidung für Requests. Wir befolgen die Spezifikation, es sei denn, wir befinden uns in einer Position, in der die Spezifikation so stark vom Verhalten in der realen Welt abweicht, dass es zu einem Problem wird (zB GET nach 302-Antwort auf einen POST).

Wenn der Upstream-Server die richtige Codierung kennt, sollte er dies signalisieren. Ansonsten werden wir den Angaben der Spezifikation folgen. =)

Wenn Sie der Meinung sind, dass der Spezifikationsstandard schlecht ist, empfehle ich Ihnen dringend, sich mit dem RFC-Prozess für HTTP/2.0 zu befassen, um diesen Standard zu ändern. =)

Was @Lukasa sagte + die Tatsache, dass wir uns auf Scharade verlassen, wenn die aus den Headern abgerufene Codierung nicht vorhanden ist, um die Codierung zu erraten. Bei so wenigen Zeichen gibt charade nichts Definitives zurück, da es statistische Daten verwendet, um zu erraten, was die richtige Codierung ist.

Ehrlich gesagt macht das Jahr keinen Unterschied und ändert auch nicht die Spezifikation.

Wenn Sie wissen, welche Codierung Sie erwarten, können Sie die Decodierung auch selbst durchführen:

text = str(r.content, '<ENCODING>', errors='replace')

Für mich ist an Anfragen nichts auszusetzen und das ist auch kein Fehler in der Scharade. Da @Lukasa mir zuzustimmen scheint, schließe ich dies.

@lavr (/cc @sigmavirus24), noch einfacher können Sie die Codierung einfach selbst bereitstellen.

>>> r = requests.get('http://irresponsible-server/')
>>> r.encoding = 'utf-8'

Fahren Sie dann normal fort.

@kennethreitz das ist enttäuschend. Warum machen wir es den Leuten so einfach? =P

Absolut :)

Hauptsächlich für japanische Websites. Sie alle lügen über ihre Kodierung.

@sigmavirus24
Bitte beachten Sie, dass utils.get_encoding_from_headers immer 'ISO-8859-1' zurückgibt und charade keine Chance hat, aufgerufen zu werden.
Der Fehler ist also: Wir erwarten, dass Scharade verwendet wird, um die Kodierung zu erraten, aber das ist nicht der Fall.

Ein obiger Patch behebt einen Fehler, folgt aber weiterhin RFC.
Bitte denken Sie daran, es zu überprüfen.

@lavr Entschuldigung, wir haben das nicht ganz klar gemacht. Wir erwarten _nicht_, dass in diesem Fall Scharade aufgerufen wird. Der RFC ist sehr klar: Wenn Sie keinen Zeichensatz angeben und der MIME-Typ text/* ist, muss als Kodierung ISO-8859-1 angenommen werden. Das bedeutet "nicht raten". =)

@lavr : r.encoding auf None und es wird wie erwartet funktionieren (glaube ich).

Oder tun Sie r.encoding = r.apparent_encoding .

Noch besser.

Bei r.encoding = None und r.encoding = r.apparent_encoding wir Server-Zeichensatzinformationen verloren.
Das völlige Ignorieren des Server-Headers ist keine gute Lösung, denke ich.

Richtige Lösung ist etwa so:

r = requests.get(...)
params = cgi.parse_header(r.headers.get('content-type'))[0]
server_encoding = ('charset' in params) and params['charset'].strip("'\"") or None
r.encoding = server_encoding or r.apparent_encoding
text = r.text

Sieht komisch aus :(

Oder tun Sie dies:

r = requests.get(...)

if r.encoding is None or r.encoding == 'ISO-8859-1':
    r.encoding = r.apparent_encoding

Ich glaube nicht :)

Bedingung r.encoding is None hat keinen Sinn, da r.encoding niemals None für content-type=text/* sein kann.

r.encoding == 'ISO-8859-1' ... was bedeutet das? Server hat Zeichensatz gesendet='ISO-8859-1' oder Server hat keinen Zeichensatz gesendet? Wenn zuerst, sollte ich den Zeichensatz nicht erraten.

@lavr Ich habe die Nicht-Text-Basen behandelt. Sie können die Möglichkeit von charset , indem Sie stattdessen diese Bedingung verwenden:

r.encoding == 'ISO-8859-1' and not 'ISO-8859-1' in r.headers.get('Content-Type', '')

@Lukasa
Nun, ich kann diesen Hack verwenden.
Und jeder in Osteuropa und Asien kann es nutzen.

Aber was ist, wenn wir es in Anfragen beheben? ;)
Was ist, wenn Anfragen ehrlich enconding=None auf Antwort ohne Zeichensatz setzen können?

Wie wir schon oft besprochen haben, folgt Requests der HTTP-Spezifikation buchstabengetreu. Das aktuelle Verhalten ist nicht falsch. =)

Die Tatsache, dass es für Ihren Anwendungsfall nicht hilfreich ist, ist eine ganz andere Geschichte. =)

Okay, das ist genug Diskussion dazu. Danke für die Rückmeldung.

Aktualisiertes HTTP 1.1 überholt ISO-8859-1-Standardzeichensatz: http://tools.ietf.org/html/rfc7231#appendix -B

Wir verfolgen dies bereits in #2086. =)

Wen es betrifft, hier ist ein Kompatibilitätspatch

Datei requests_patch.py mit folgendem Code erstellen und importieren, dann sollte das Problem gelöst sein.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import chardet

def monkey_patch():
    prop = requests.models.Response.content
    def content(self):
        _content = prop.fget(self)
        if self.encoding == 'ISO-8859-1':
            encodings = requests.utils.get_encodings_from_content(_content)
            if encodings:
                self.encoding = encodings[0]
            else:
                self.encoding = chardet.detect(_content)['encoding']

            if self.encoding:
                _content = _content.decode(self.encoding, 'replace').encode('utf8', 'replace')
                self._content = _content
                self.encoding = 'utf8'

        return _content
    requests.models.Response.content = property(content)

monkey_patch()


@lavr (/cc @sigmavirus24), noch einfacher können Sie die Codierung einfach selbst bereitstellen.

>>> r = requests.get('http://irresponsible-server/')
>>> r.encoding = 'utf-8'

Fahren Sie dann normal fort.

Danke dafür! Hast du eine Idee, wie man das in einer Zeile macht?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

Matt3o12 picture Matt3o12  ·  3Kommentare

ReimarBauer picture ReimarBauer  ·  4Kommentare

NoahCardoza picture NoahCardoza  ·  4Kommentare

xsren picture xsren  ·  3Kommentare

JimHokanson picture JimHokanson  ·  3Kommentare