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?
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 ).
Comentários muito úteis
Eu fiz isso no passado