Requests: Gezippte Antworten streamen

Erstellt am 31. Juli 2014  ·  10Kommentare  ·  Quelle: psf/requests

Ich muss große XML-Antworten als Stream verarbeiten. Die unkomprimierten Antworten können mehrere hundert Megabyte groß sein, daher ist es keine Option, sie vollständig in den Speicher zu laden, bevor sie an den XML-Parser übergeben werden.

Ich verwende lxml zum Analysieren und übergebe einfach response.raw an seine iterparse() Funktion, wie irgendwo in den Anforderungsdokumenten beschrieben. Dies funktioniert gut für unkomprimierte Antworten.

Leider ist die API, die ich aufrufe, nicht besonders gut. Daher wird manchmal Content-Encoding: gzip auch wenn ich explizit nach unkomprimierten Daten frage. Außerdem ist die Komprimierungsrate dieser sich extrem wiederholenden und ausführlichen XML-Dateien wirklich gut (10x+), daher würde ich wirklich gerne komprimierte Antworten verwenden.

Ist das bei Anfragen möglich? In der Dokumentation konnte ich es nicht finden. Wenn man tiefer in urllib3 recherchiert, scheint seine HTTPResponse.read()- Methode einen decode_content Parameter zu unterstützen. Wenn nicht gesetzt, greift urllib3 auf das zurück, was im Konstruktor festgelegt ist. Wenn Anfragen den Konstruktor in request.adapters.HTTPAdapter.send() aufrufen, wird decode_content explizit auf False gesetzt.

Gibt es einen Grund, warum Anfragen dies tun?

Seltsamerweise setzt iter_content() decode_content=True beim Lesen tatsächlich
Persönlich kann ich iter_content() natürlich nicht wirklich verwenden, weil ich ein dateiähnliches Objekt für lxml benötige.

Ich habe zuvor mein eigenes dateiähnliches Objekt geschrieben, das ich zwischen Anfragen und lxml einhängen kann, aber natürlich ist das Puffern schwer und ich fühle mich wie schlauere Leute als ich das zuvor geschrieben habe, also würde ich es vorziehen, nicht mein eigenes zu rollen .

Was ist Ihr Rat, wie man damit umgeht? Sollten Anfragen auf die Standardeinstellung decode_content=True in urllib3 geändert werden?

Contributor Friendly Documentation Planned

Hilfreichster Kommentar

Ich habe das in der Vergangenheit gemacht

r = requests.get('url', stream=True)
r.raw.decode_content = True
...

Alle 10 Kommentare

Nein, dies sollte aus einer Vielzahl von Gründen nicht standardmäßig eingestellt werden. Was Sie tun sollten, ist functools.partial zu verwenden, um die Methode read in der Antwort zu ersetzen (oder sie einfach anders zu umschließen), sodass Sie Folgendes tun:

response.raw.read = functools.partial(response.raw.read, decode_content=True)

und übergeben Sie dann response.raw an Ihren Parser.

@sigmavirus24 Danke, das ist definitiv eine elegante Lösung für das oben beschriebene Problem!

Ich würde empfehlen, dies in die Dokumentation der Anfragen aufzunehmen, z. B. in den FAQ: http://docs.python-requests.org/en/latest/community/faq/#encoded -data
Derzeit ist die Aussage "Anfragen automatisch dekomprimiert gzip-codierte Antworten" für den Fall stream=True nicht korrekt und kann zu Überraschungen führen.

Was mein Problem angeht , wie Sie in der haben , hat die urllib3-Implementierung der gzip-Dekomprimierung ihre eigenen kleinen Macken, die ich in meinem Code umgehen muss, aber das ist kein Problem mehr für Anfragen.

aber das ist bei Anfragen kein Problem mehr.

Wie in Ihrer Meinung nach kann dies geschlossen werden?

@sigmavirus24 Ich glaube, es sollte dokumentiert werden, da die aktuelle Dokumentation falsch ist.

Aber wenn Sie damit nicht einverstanden sind, ja, in der Nähe!

Die Dokumentation könnte übersichtlicher sein. Für mich (und das liegt ausschließlich daran, dass ich ein Core-Entwickler bin) spricht der erste Absatz die 90% der Benutzer an, die niemals die rohe Antwort berühren, während der zweite Absatz dem ersten widerspricht, indem er sagt: "Aber wenn Sie darauf zugreifen müssen Rohdaten, sie sind für Sie da". Wie gesagt, das ist für mich offensichtlich, aber ich kann sehen, wie das klarer gemacht werden könnte. Daran arbeite ich heute Abend.

Für mich ist es eher so, dass ich "Rohdaten" als "Rohdaten" interpretiert hätte, also einen dekomprimierten Stream. Ich muss es nur in allen Teilen lesen, die ich brauche. Im Gegensatz zu .content , das ein dekomprimierter Blob ist (ebenfalls die Nutzlast, aber in einer anderen Form).

Die eigentliche Dekomprimierung fühlt sich für mich wie ein Problem der HTTP-Bibliothek an – ein Implementierungsdetail von HTTP, wenn Sie so wollen, eines, von dem ich erwarten würde, dass Anfragen abstrahiert werden. Ob ich die Nutzlast von Anfragen als Stream oder als vorab abgerufenes Datenblob lese, würde keinen Unterschied machen. In jedem Fall würden Anforderungen das Implementierungsdetail "Komprimierung" abstrahieren.

(Diese Annahme war auch der Kern meiner ursprünglichen Anfrage, decode_content standardmäßig auf True . Jetzt, da ich sehe, was für eine undichte Abstraktion das ist, schlage ich das natürlich nicht mehr vor.)

Aber ja, ich stimme absolut zu, dass 99% Ihrer Benutzer von diesem Detail nie betroffen sein werden.

Gerne können Sie dieses Thema schließen.

Das führt also tatsächlich zu etwas, das mir schon seit einiger Zeit im Kopf herumschwirrt und das ich noch nicht vorgeschlagen habe, weil es eine signifikante API-Änderung wäre.

Ich mag die Tatsache nicht, dass wir vorschlagen, dass die Leute r.raw weil es ein Objekt ist, das wir nicht dokumentieren und es ist ein Objekt, das von urllib3 bereitgestellt wird (was wir in der Vergangenheit behauptet haben, ist eher ein Implementierungsdetail). Vor diesem Hintergrund habe ich mit der Idee gespielt, Methoden für ein Response Objekt bereitzustellen, die nur die urllib3 Methoden ersetzen ( read würde nur raw.read usw.). Dies gibt uns zusätzliche Flexibilität um urllib3 und ermöglicht es uns (im Namen der Benutzer) eine API-Änderung in urllib3 (was in der Vergangenheit fast nie ein Problem war, also gibt es keine Dringlichkeit darin).

Trotzdem haben wir meiner Meinung nach bereits genug Methoden für ein Response-Objekt, und die Erweiterung unserer API ist nicht ideal. Die beste API ist die API, von der nichts mehr zu entfernen ist. Also bin ich ständig am Zaun darüber.


Diese Annahme war auch der Kern meiner ursprünglichen Anfrage, decode_content auf True zu setzen. Jetzt, da ich sehe, was für eine undichte Abstraktion das ist, behaupte ich das natürlich nicht mehr.

Für andere, die dies finden und sich möglicherweise nicht sicher sind, warum dies wahr ist, erlauben Sie mir, es zu erklären.

Es gibt mehrere Benutzer von Anfragen, die die automatische Dekompression deaktivieren, um die Länge einer Antwort zu überprüfen oder andere wichtige Dinge damit zu tun. Ein Verbraucher der ersteren Art ist OpenStack. Viele der OpenStack-Clients validieren den an den Client gesendeten Content-Length Header und die tatsächliche Länge des empfangenen Bodys. Für sie ist die Dekompression ein fairer Kompromiss, um sicher zu sein, dass sie eine gültige Antwort erhalten und verarbeiten.

Ein weiterer Verbraucher ist Betamax (oder wirklich jedes Tool, das Antwortobjekte (re)konstruiert), denn wenn es den vollständigen Prozess einer vollständig gültigen Antwort abwickelt, muss der Inhalt im komprimierten Format vorliegen.

Ich bin mir sicher, dass es andere gibt, die weder @Lukasa noch ich kennen, die sich ebenfalls stark auf dieses Verhalten verlassen.

Habe heute das gleiche Problem getroffen und bin zu derselben Annahme gekommen, da es derzeit keine andere Möglichkeit gibt, Antworten zu streamen.

Warum nicht ein einzelnes neues Attribut, z. B. response.stream das die gleiche Rolle als Proxy für .raw spielen würde, anstatt mehrerer neuer Methoden auf Response? Es würde auch die Einstellung / den Parameter von stream=True widerspiegeln und würde keine Auswirkungen auf Benutzer haben, die das aktuelle Verhalten von .raw benötigen.

Ich habe das in der Vergangenheit gemacht

r = requests.get('url', stream=True)
r.raw.decode_content = True
...

Beachten Sie, dass die Umgehung von @ sigmavirus24 bricht die Semantik der tell Methode, die falschen Offsets zurück.

Ich bin darauf gestoßen, als ich eine Antwort als fortsetzbaren Upload in die Google Cloud Storage API gestreamt habe, die tell() , um die Anzahl der gerade gelesenen Bytes zu ermitteln ( hier ).

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen