Requests: Streaming tanggapan yang di-gzip

Dibuat pada 31 Jul 2014  ·  10Komentar  ·  Sumber: psf/requests

Saya perlu memproses respons XML besar sebagai aliran. Respons yang tidak terkompresi dapat berukuran beberapa ratus megabita, jadi memuatnya seluruhnya ke dalam memori sebelum menyerahkannya ke parser XML bukanlah suatu pilihan.

Saya menggunakan lxml untuk mengurai dan saya hanya menyerahkan response.raw ke fungsi iterparse() , seperti yang dijelaskan di suatu tempat di dokumen permintaan. Ini berfungsi dengan baik untuk respons yang tidak terkompresi.

Sayangnya, API yang saya panggil tidak terlalu bagus. Jadi kadang-kadang akan mengembalikan Content-Encoding: gzip bahkan jika saya secara eksplisit meminta data yang tidak terkompresi. Juga, rasio kompresi pada file XML yang sangat berulang dan bertele-tele ini sangat bagus (10x+), jadi saya sangat ingin memanfaatkan respons terkompresi.

Apakah ini mungkin dengan permintaan? Saya tidak dapat menemukannya di dokumentasi. Meneliti lebih dalam tentang urllib3, metode HTTPResponse.read() tampaknya mendukung parameter decode_content . Jika tidak disetel, urllib3 kembali ke apa yang disetel di konstruktor. Saat request memanggil konstruktor di request.adapters.HTTPAdapter.send() , itu secara eksplisit menetapkan decode_content ke False.

Apakah ada alasan mengapa permintaan melakukan itu?

Anehnya, iter_content() sebenarnya menyetel decode_content=True saat membaca. Kenapa disini? Semuanya tampak agak sewenang-wenang. Saya tidak begitu mengerti motivasi untuk melakukannya dengan satu cara di sini dan dengan cara lain di sana.
Secara pribadi, saya tidak dapat benar-benar menggunakan iter_content() tentu saja karena saya memerlukan objek seperti file untuk lxml.

Saya sebelumnya menulis objek seperti file saya sendiri yang dapat saya kaitkan di antara permintaan dan lxml, tetapi tentu saja buffering itu sulit dan saya merasa seperti orang yang lebih pintar daripada saya yang telah menulis ini sebelumnya, jadi saya lebih suka tidak harus menggulung sendiri .

Apa saran Anda bagaimana menangani ini? Haruskah permintaan diubah ke default ke pengaturan decode_content=True di urllib3?

Contributor Friendly Documentation Planned

Komentar yang paling membantu

Saya telah melakukan ini di masa lalu

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

Semua 10 komentar

Tidak, seharusnya tidak default ke pengaturan itu karena berbagai alasan. Yang harus Anda lakukan adalah menggunakan functools.partial untuk mengganti metode read pada respons (atau cukup bungkus dengan cara lain) sehingga Anda melakukan sesuatu seperti:

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

dan kemudian berikan response.raw ke parser Anda.

@ sigmavirus24 Terima kasih, itu jelas merupakan solusi elegan untuk masalah yang saya uraikan di atas!

Saya akan merekomendasikan menambahkan itu ke dokumentasi permintaan, misalnya di FAQ: http://docs.python-requests.org/en/latest/community/faq/#encoded -data
Saat ini, pernyataan "Permintaan secara otomatis mendekompresi tanggapan yang dikodekan gzip" tidak benar untuk kasus stream=True dan dapat menyebabkan kejutan.

Adapun masalah saya, seperti yang telah Anda baca di masalah urllib3 , implementasi urllib3 dari dekompresi gzip memiliki sedikit kebiasaan yang harus saya atasi dalam kode saya, tetapi itu tidak lagi menjadi masalah untuk permintaan.

tapi itu tidak lagi menjadi masalah untuk permintaan.

Seperti di Anda merasa ini bisa ditutup?

@ sigmavirus24 Saya percaya itu harus didokumentasikan, karena dokumentasi saat ini salah.

Tetapi jika Anda tidak setuju dengan itu, ya, tutup!

Dokumentasi bisa lebih jelas. Bagi saya (dan ini sepenuhnya karena saya adalah pengembang inti) paragraf pertama berbicara kepada 90% pengguna yang tidak akan pernah menyentuh respons mentah, sedangkan paragraf kedua bertentangan dengan yang pertama dengan mengatakan "tetapi jika Anda perlu mengakses data mentah, itu ada untuk Anda". Seperti yang saya katakan, itu jelas bagi saya, tetapi saya dapat melihat bagaimana hal itu dapat dibuat lebih jelas. Aku akan mengerjakannya malam ini.

Bagi saya, lebih dari itu saya akan menafsirkan "data mentah" sebagai "muatan mentah", yaitu aliran yang didekompresi. Saya hanya perlu membacanya dalam potongan apa pun yang saya butuhkan. Berbeda dengan .content , yang merupakan gumpalan yang didekompresi (juga muatannya, tetapi dalam bentuk yang berbeda).

Dekompresi yang sebenarnya terasa seperti masalah perpustakaan HTTP bagi saya — detail implementasi HTTP jika Anda mau, yang saya harapkan permintaan untuk diabstraksikan. Apakah saya membaca muatan dari permintaan sebagai aliran atau sebagai gumpalan data yang diambil sebelumnya tidak akan membuat perbedaan. Either way, permintaan akan mengabstraksi 'kompresi' detail implementasi.

(Asumsi ini juga merupakan inti dari permintaan awal saya untuk default decode_content ke True . Tentu saja sekarang setelah saya melihat abstraksi yang bocor ini, saya tidak lagi menyarankan itu.)

Tapi ya, saya sangat setuju bahwa 99% pengguna Anda tidak akan pernah terpengaruh oleh detail ini.

Jangan ragu untuk menutup masalah ini.

Jadi ini sebenarnya mengarah pada sesuatu yang telah berkecamuk di kepala saya untuk sementara waktu dan yang belum saya usulkan karena itu akan menjadi perubahan API yang signifikan.

Saya tidak suka fakta bahwa kami menyarankan orang menggunakan r.raw karena itu adalah objek yang tidak kami dokumentasikan dan itu adalah objek yang disediakan oleh urllib3 (yang telah kami klaim di masa lalu adalah lebih detail implementasi). Dengan mengingat hal itu, saya telah mempermainkan ide untuk menyediakan metode pada objek Response yang hanya diproksi ke metode urllib3 ( read hanya akan diproksi ke raw.read , dll.). Ini memberi kami fleksibilitas ekstra sekitar urllib3 dan memungkinkan kami untuk menangani (atas nama pengguna) perubahan API di urllib3 (yang secara historis hampir tidak pernah menjadi masalah, jadi tidak ada urgensi dalam hal itu).

Dengan itu, kami sudah memiliki cukup metode pada objek Response menurut saya dan mengembangkan API kami tidak ideal. API terbaik adalah API dari mana tidak ada yang tersisa untuk dihapus. Jadi saya terus-menerus di pagar tentang hal ini.


Asumsi ini juga merupakan inti dari permintaan awal saya untuk default decode_content ke True. Tentu saja sekarang setelah saya melihat abstraksi yang bocor ini, saya tidak lagi menyarankan itu.

Bagi orang lain yang menemukan ini dan mungkin tidak yakin mengapa ini benar, izinkan saya menjelaskannya.

Ada beberapa pengguna permintaan yang menonaktifkan dekompresi otomatis untuk memvalidasi panjang respons, atau untuk melakukan hal penting lainnya dengannya. Salah satu konsumen dari jenis sebelumnya adalah OpenStack. Banyak klien OpenStack memvalidasi header Content-Length dikirim ke klien dan panjang sebenarnya dari body yang diterima. Bagi mereka, menangani dekompresi adalah pertukaran yang adil untuk memastikan mereka menerima dan menangani respons yang valid.

Konsumen lain adalah Betamax (atau benar-benar alat apa pun yang (kembali) membangun objek Respons) karena ketika menangani proses penuh membuat respons yang benar-benar valid, konten harus dalam format terkompresi.

Saya yakin ada orang lain yang @Lukasa atau saya tidak tahu tentang itu juga sangat bergantung pada perilaku ini.

Mendapat masalah yang sama hari ini, dan akhirnya membuat asumsi yang sama karena tidak ada cara lain untuk mengalirkan tanggapan saat ini.

Daripada beberapa metode baru pada Response mengapa tidak satu atribut baru misalnya response.stream yang akan memainkan peran yang sama dari proxy ke .raw ? Itu juga akan mencerminkan pengaturan/parameter stream=True , dan tidak akan memengaruhi pengguna yang membutuhkan perilaku .raw .

Saya telah melakukan ini di masa lalu

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

Perhatikan bahwa solusi oleh @sigmavirus24 merusak semantik metode tell , yang akan mengembalikan offset yang salah.

Saya mengalami ini ketika mengalirkan respons sebagai unggahan yang dapat dilanjutkan ke Google Cloud Storage API, yang menggunakan tell() untuk mengetahui jumlah byte yang baru saja dibaca ( di sini ).

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

remram44 picture remram44  ·  4Komentar

brainwane picture brainwane  ·  3Komentar

JimHokanson picture JimHokanson  ·  3Komentar

cnicodeme picture cnicodeme  ·  3Komentar

iLaus picture iLaus  ·  3Komentar