Requests: Respostas compactadas em streaming

Criado em 31 jul. 2014  ·  10Comentários  ·  Fonte: psf/requests

Preciso processar grandes respostas XML como um fluxo. As respostas descompactadas podem ter centenas de megabytes de tamanho, portanto, carregá-las inteiramente na memória antes de entregá-las ao analisador XML não é uma opção.

Estou usando lxml para analisar e apenas entrego response.raw para sua função iterparse() , conforme descrito em algum lugar nos documentos de solicitações. Isso funciona bem para respostas não compactadas.

Infelizmente, a API que estou chamando não é muito boa. Portanto, às vezes, ele retornará Content-Encoding: gzip mesmo se eu solicitar explicitamente os dados não compactados. Além disso, a taxa de compactação nesses arquivos XML extremamente repetitivos e detalhados é muito boa (10x +), então eu realmente gostaria de fazer uso de respostas compactadas.

Isso é possível com solicitações? Não consegui encontrar na documentação. Pesquisando mais profundamente em urllib3, seu método HTTPResponse.read () parece suportar um parâmetro decode_content . Se não for definido, o urllib3 retorna ao que está definido no construtor. Quando as solicitações chama o construtor em requests.adapters.HTTPAdapter.send () , ele define explicitamente decode_content como False.

Existe uma razão pela qual os pedidos fazem isso?

Estranhamente, iter_content() realmente define decode_content=True durante a leitura. Porque aqui? Tudo parece um pouco arbitrário. Eu realmente não entendo a motivação de fazer isso de uma forma aqui e outra ali.
Pessoalmente, não posso usar iter_content() claro, porque preciso de um objeto semelhante a um arquivo para lxml.

Já escrevi meu próprio objeto semelhante a um arquivo que posso conectar entre as solicitações e o lxml, mas é claro que o armazenamento em buffer é difícil e acho que pessoas mais espertas do que eu já escreveram isso antes, então prefiro não ter que fazer o meu próprio .

Qual é o seu conselho sobre como lidar com isso? As solicitações devem ser alteradas para o padrão para definir decode_content=True em urllib3?

Contributor Friendly Documentation Planned

Comentários muito úteis

Eu fiz isso no passado

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

Todos 10 comentários

Não, não deve ser definido como padrão por uma ampla variedade de razões. O que você deve fazer é usar functools.partial para substituir o método read na resposta (ou apenas envolvê-lo de outra forma) para que você faça algo como:

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

e então passe response.raw para o seu analisador.

@ sigmavirus24 Obrigado, essa é definitivamente uma solução elegante para o problema que descrevi acima!

Eu recomendaria adicionar isso à documentação das solicitações, por exemplo, no FAQ: http://docs.python-requests.org/en/latest/community/faq/#encoded -data
Atualmente, a declaração "Solicitações descompacta automaticamente as respostas codificadas em gzip" não é correta para o caso stream=True e pode levar a surpresas.

Quanto ao meu problema, como você leu sobre a questão urllib3 , a implementação urllib3 da descompressão gzip tem suas próprias pequenas peculiaridades que tenho que contornar em meu código, mas isso não é mais um problema para solicitações.

mas isso não é mais um problema para pedidos.

Como você sente que isso pode ser fechado?

@ sigmavirus24 Acredito que deva ser documentado, pois a documentação atual está incorreta.

Mas se você discorda disso, sim, feche!

A documentação poderia ser mais clara. Para mim (e isso é inteiramente porque sou um desenvolvedor central), o primeiro parágrafo fala para 90% dos usuários que nunca tocarão na resposta bruta, enquanto o segundo parágrafo contradiz o primeiro ao dizer "mas se você precisar acessar o dados brutos, estão lá para você ". Como eu disse, isso é aparente para mim, mas posso ver como isso pode ficar mais claro. Vou trabalhar nisso esta noite.

Para mim, é mais do que interpretar "dados brutos" como "carga útil bruta", ou seja, um fluxo descompactado. Eu só tenho que ler em todos os pedaços que eu precisar. Ao contrário de .content , que é um blob descompactado (também a carga útil, mas em uma forma diferente).

A descompactação real parece uma preocupação da biblioteca HTTP para mim - um detalhe de implementação de HTTP se você quiser, um que eu esperaria que as solicitações fossem abstraídas. Se eu ler a carga útil das solicitações como um fluxo ou como um blob pré-buscado de dados, não faria diferença. De qualquer maneira, as solicitações abstraíam a 'compactação' dos detalhes de implementação.

(Essa suposição também estava no cerne da minha solicitação original para padronizar decode_content para True . É claro que agora que vejo que abstração vazada é essa, não estou mais sugerindo isso.)

Mas sim, concordo absolutamente que 99% dos seus usuários nunca serão afetados por esse detalhe.

Sinta-se à vontade para encerrar este problema.

Então, isso realmente leva a algo que está martelando na minha cabeça por um tempo e que eu ainda não propus porque seria uma mudança significativa na API.

Não gosto do fato de sugerirmos que as pessoas usem r.raw porque é um objeto que não documentamos e é um objeto fornecido por urllib3 (que afirmamos no passado ser mais um detalhe de implementação). Com isso em mente, estou brincando com a ideia de fornecer métodos em um objeto Response que apenas proxy para urllib3 métodos ( read apenas proxy para raw.read , etc.). Isso nos dá flexibilidade extra em torno de urllib3 e nos permite lidar (em nome dos usuários) com uma mudança de API em urllib3 (que historicamente quase nunca foi um problema, então não há urgência nisso).

Dito isso, já temos métodos suficientes em um objeto Response na minha opinião e aumentar nossa API não é o ideal. A melhor API é aquela da qual não há mais nada a ser removido. Portanto, estou continuamente em dúvida sobre isso.


Essa suposição também estava no cerne da minha solicitação original para definir decode_content como True. Claro, agora que vejo que abstração vazada é essa, não estou mais sugerindo isso.

Para outras pessoas que acham isso e podem não ter certeza por que isso é verdade, deixe-me explicar.

Existem vários usuários de solicitações que desativam a descompressão automática para validar a duração de uma resposta ou para fazer outras coisas importantes com ela. Um consumidor do primeiro tipo é o OpenStack. Muitos dos clientes OpenStack validam o cabeçalho Content-Length enviado ao cliente e o comprimento real do corpo recebido. Para eles, lidar com a descompressão é uma troca justa para ter certeza de que estão recebendo e lidando com uma resposta válida.

Outro consumidor é o Betamax (ou realmente qualquer ferramenta que (re) construa objetos Response) porque quando está lidando com todo o processo de fazer uma resposta totalmente válida, ele precisa que o conteúdo esteja no formato compactado.

Tenho certeza de que há outros que nem @Lukasa nem eu conhecemos e que também dependem muito desse comportamento.

Tive o mesmo problema hoje e acabei fazendo a mesma suposição, pois não há outra maneira de transmitir respostas no momento.

Em vez de vários novos métodos de resposta, por que não um único novo atributo, por exemplo, response.stream que desempenharia a mesma função de proxy para .raw ? Ele também espelharia muito bem a configuração / parâmetro stream=True e não afetaria os usuários que precisam do comportamento .raw atual.

Eu fiz isso no passado

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

Observe que a solução alternativa por @ sigmavirus24 quebra a semântica do método tell , que retornará deslocamentos incorretos.

Eu encontrei isso ao transmitir uma resposta como um upload recuperável na API do Google Cloud Storage, que usa tell() para descobrir o número de bytes que foram lidos ( aqui ).

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

remram44 picture remram44  ·  4Comentários

avinassh picture avinassh  ·  4Comentários

JimHokanson picture JimHokanson  ·  3Comentários

jakul picture jakul  ·  3Comentários

xsren picture xsren  ·  3Comentários