Requests: Потоковая передача сжатых ответов

Созданный на 31 июл. 2014  ·  10Комментарии  ·  Источник: psf/requests

Мне нужно обрабатывать большие XML-ответы в виде потока. Несжатые ответы могут иметь размер в несколько сотен мегабайт, поэтому полностью загружать их в память перед передачей синтаксическому анализатору XML не представляется возможным.

Я использую lxml для синтаксического анализа и просто передаю response.raw его функции iterparse() , как описано где-то в документации по запросам. Это отлично подходит для несжатых ответов.

К сожалению, API, который я вызываю, не очень хорош. Поэтому иногда он возвращает Content-Encoding: gzip даже если я явно запрашиваю несжатые данные. Кроме того, степень сжатия этих чрезвычайно повторяющихся и подробных XML-файлов действительно хороша (10x +), поэтому я бы очень хотел использовать сжатые ответы.

Возможно ли это с запросами? Я не нашел его в документации. При более глубоком изучении urllib3 его метод HTTPResponse.read (), похоже, поддерживает параметр decode_content . Если не установлен, urllib3 возвращается к тому, что установлено в конструкторе. Когда запросы вызывают конструктор в request.adapters.HTTPAdapter.send () , он явно устанавливает для decode_content значение False.

Есть ли причина, по которой запросы делают это?

Как ни странно, iter_content() самом деле устанавливает decode_content=True при чтении. Почему здесь? Все это кажется немного произвольным. Я действительно не понимаю мотивацию делать это здесь так, а там иначе.
Лично я, конечно, не могу использовать iter_content() потому что мне нужен файловый объект для lxml.

Я ранее писал свой собственный файловый объект, который я могу подключать между запросами и lxml, но, конечно, буферизация сложна, и я чувствую, что люди более умные, чем я, писали это раньше, поэтому я бы предпочел не катить свой собственный .

Что вы посоветуете, как с этим справиться? Следует ли изменить запросы по умолчанию на установку decode_content=True в urllib3?

Contributor Friendly Documentation Planned

Самый полезный комментарий

Я делал это в прошлом

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

Все 10 Комментарий

Нет, он не должен устанавливать это по умолчанию по целому ряду причин. Что вам нужно сделать, так это использовать functools.partial для замены метода read в ответе (или просто обернуть его другим способом), чтобы вы сделали что-то вроде:

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

а затем передайте парсеру response.raw .

@ sigmavirus24 Спасибо, это определенно элегантное решение проблемы, о которой я говорил выше!

Я бы рекомендовал добавить это в документацию по запросам, например, в FAQ: http://docs.python-requests.org/en/latest/community/faq/#encoded -data
В настоящее время утверждение «Запросы автоматически распаковывают ответы в формате gzip» неверно для случая stream=True и может привести к неожиданностям.

Что касается моей проблемы, как вы читали о проблеме urllib3, реализация распаковки gzip urllib3 имеет свои собственные небольшие особенности, которые я должен исправить в моем коде, но это больше не проблема для запросов.

но это больше не проблема для запросов.

Как вы думаете, это можно закрыть?

@ sigmavirus24 Я считаю, что это следует задокументировать, поскольку текущая документация неверна.

Но если вы не согласны с этим, да, закройте!

Документация могла бы быть более понятной. Для меня (и это полностью потому, что я основной разработчик) первый абзац обращается к 90% пользователей, которые никогда не коснутся необработанного ответа, а второй абзац противоречит первому, говоря «но если вам нужно получить доступ к сырые данные, они есть для вас ". Как я уже сказал, это очевидно для меня, но я вижу, как это можно прояснить. Я поработаю над этим сегодня вечером.

Для меня это больше, что я бы интерпретировал «необработанные данные» как «необработанные данные», то есть распакованный поток. Мне просто нужно прочитать это по частям, которые мне нужны. В отличие от .content , который представляет собой распакованный BLOB-объект (также полезная нагрузка, но в другой форме).

Фактическая распаковка кажется мне проблемой библиотеки HTTP - деталью реализации HTTP, если хотите, которая, как я ожидаю, абстрагируется от запросов. Независимо от того, читаю ли я полезную нагрузку из запросов как поток или как предварительно полученный блок данных, не имеет значения. В любом случае запросы будут абстрагироваться от «сжатия» деталей реализации.

(Это предположение также лежало в основе моего первоначального запроса на установку по умолчанию decode_content на True . Конечно, теперь, когда я вижу, что это за ненадежная абстракция, я больше не предлагаю этого.)

Но да, я абсолютно согласен с тем, что 99% ваших пользователей никогда не будут затронуты этой деталью.

Не стесняйтесь закрывать этот выпуск.

Так что это на самом деле приводит к тому, что какое-то время вертится у меня в голове и чего я еще не предлагал, потому что это будет существенное изменение API.

Мне не нравится тот факт, что мы предлагаем людям использовать r.raw потому что это объект, который мы не документируем, и это объект, предоставляемый urllib3 (который, как мы заявляли ранее, подробнее о реализации). Имея это в виду, я раздумывал над идеей предоставления методов для объекта Response которые просто прокси для urllib3 методов ( read будет просто прокси для raw.read и т. Д.). Это дает нам дополнительную гибкость в отношении urllib3 и позволяет нам обрабатывать (от имени пользователей) изменение API в urllib3 (что исторически почти никогда не было проблемой, поэтому нет никаких срочность в этом).

С учетом сказанного, на мой взгляд, у нас уже достаточно методов для объекта Response, и расширение нашего API не является идеальным. Лучший API - это тот API, из которого уже нечего удалять. Так что я постоянно сомневаюсь в этом.


Это предположение также лежало в основе моего первоначального запроса на значение по умолчанию для decode_content значение True. Конечно, теперь, когда я вижу, какая это дырявая абстракция, я больше не предлагаю этого.

Для тех, кто находит это и может не знать, почему это правда, позвольте мне объяснить.

Есть несколько пользователей запросов, которые отключают автоматическую декомпрессию, чтобы проверить длину ответа или сделать с ним другие важные вещи. Одним из потребителей первого типа является OpenStack. Многие клиенты OpenStack проверяют отправленный клиенту заголовок Content-Length и фактическую длину полученного тела. Для них обработка декомпрессии - это справедливый компромисс, позволяющий быть уверенным, что они получают и обрабатывают правильный ответ.

Другой потребитель - это Betamax (или любой другой инструмент, который (пере) конструирует объекты Response), потому что, когда он обрабатывает полный процесс создания полностью действительного ответа, ему нужно, чтобы контент был в сжатом формате.

Я уверен, что есть другие, о которых ни @Lukasa, ни я не знаем, также сильно полагаются на такое поведение.

Решили ту же проблему сегодня и в итоге сделали то же предположение, что на данный момент другого способа потоковой передачи ответов нет.

Вместо нескольких новых методов в Response, почему бы не один новый атрибут, например, response.stream который играл бы ту же роль прокси для .raw ? Это также хорошо отражает параметр / параметр stream=True и не влияет на пользователей, которым требуется текущее поведение .raw .

Я делал это в прошлом

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

Обратите внимание, что обходной путь @ sigmavirus24 нарушает семантику метода tell , который возвращает неправильные смещения.

Я столкнулся с этим при потоковой передаче ответа в виде возобновляемой загрузки в Google Cloud Storage API, который использует tell() для определения количества только что прочитанных байтов ( здесь ).

Была ли эта страница полезной?
0 / 5 - 0 рейтинги