Solicitud de http con tiempo de espera, tamaño máximo y agrupación de conexiones

Estoy buscando una manera en Python (2.7) para hacer solicitudes HTTP con 3 requisitos:

  • tiempo de espera (por fiabilidad)
  • Tamaño máximo del contenido (por seguridad)
  • agrupación de conexiones (para el rendimiento)

He comprobado bastante todas las bibliotecas HTTP de Python, pero ninguna de ellas cumple mis requisitos. Por ejemplo:

urllib2: bueno, pero sin agrupación

import urllib2 import json r = urllib2.urlopen('https://github.com/timeline.json', timeout=5) content = r.read(100+1) if len(content) > 100: print 'too large' r.close() else: print json.loads(content) r = urllib2.urlopen('https://github.com/timeline.json', timeout=5) content = r.read(100000+1) if len(content) > 100000: print 'too large' r.close() else: print json.loads(content) 

solicitudes: sin tamaño máximo

 import requests r = requests.get('https://github.com/timeline.json', timeout=5, stream=True) r.headers['content-length'] # does not exists for this request, and not safe content = r.raw.read(100000+1) print content # ARF this is gzipped, so not the real size print json.loads(content) # content is gzipped so pretty useless print r.json() # Does not work anymore since raw.read was used 

urllib3: nunca funcionó el método de “lectura”, incluso con un archivo de 50Mo …

httplib: httplib.HTTPConnection no es un grupo (solo una conexión)

¡Apenas puedo creer que urllib2 es la mejor biblioteca HTTP que puedo usar! Entonces, si alguien sabe qué librairy puede hacer esto o cómo usar uno de los librairy anteriores …

EDITAR:

La mejor solución que encontré gracias a Martijn Pieters (StringIO no disminuye la velocidad incluso para archivos grandes, donde la adición de str hace mucho).

 r = requests.get('https://github.com/timeline.json', stream=True) size = 0 ctt = StringIO() for chunk in r.iter_content(2048): size += len(chunk) ctt.write(chunk) if size > maxsize: r.close() raise ValueError('Response too large') content = ctt.getvalue() 

Puedes hacerlo con requests muy bien; pero necesita saber que el objeto sin raw es parte de las tripas de urllib3 y hacer uso de los argumentos adicionales que admite la llamada HTTPResponse.read() , que le permiten especificar que desea leer datos decodificados :

 import requests r = requests.get('https://github.com/timeline.json', timeout=5, stream=True) content = r.raw.read(100000+1, decode_content=True) if len(content) > 100000: raise ValueError('Too large a response') print content print json.loads(content) 

Alternativamente, puede establecer la decode_content en el objeto en raw antes de leer:

 import requests r = requests.get('https://github.com/timeline.json', timeout=5, stream=True) r.raw.decode_content = True content = r.raw.read(100000+1) if len(content) > 100000: raise ValueError('Too large a response') print content print json.loads(content) 

Si no te gusta llegar a urllib3 tripas de urllib3 esa manera, usa response.iter_content() para iterar sobre el contenido descodificado en fragmentos; esto también usa el HTTPResponse subyacente (usando la versión del generador .stream() :

 import requests r = requests.get('https://github.com/timeline.json', timeout=5, stream=True) maxsize = 100000 content = '' for chunk in r.iter_content(2048): content += chunk if len(content) > maxsize: r.close() raise ValueError('Response too large') print content print json.loads(content) 

Hay una diferencia sutil aquí en cómo se manejan los tamaños de datos comprimidos aquí; r.raw.read(100000+1) solo leerá 100k bytes de datos comprimidos; los datos sin comprimir se prueban contra su tamaño máximo. El método iter_content() leerá más datos sin comprimir en el caso raro de que la secuencia comprimida sea más grande que los datos sin comprimir .

Ninguno de los métodos permite que r.json() funcione; el atributo response._content no está establecido por estos; Por supuesto, puedes hacerlo manualmente. Pero dado que las .raw.read() y .iter_content() ya le dan acceso al contenido en cuestión, realmente no hay necesidad.