Usar urllibs
(o urllibs2
) y querer lo que quiero es inútil. ¿Alguna solución?Python - buscar en la corriente de respuesta http
Respuesta
No estoy seguro de cómo funciona la aplicación C#, pero, como corrientes de Internet en general, no son buscable, supongo que descarga todos los datos a un archivo local o un objeto en memoria y busca desde allí. El equivalente de Python de esto sería hacer lo que Abafei sugirió y escribir los datos en un archivo o StringIO y buscar desde allí.
Sin embargo, si, como sugiere su comentario sobre la respuesta de Abafei, desea recuperar solo una parte particular del archivo (en lugar de buscar hacia atrás y hacia adelante a través de los datos devueltos), existe otra posibilidad. urllib2
se puede utilizar para recuperar cierta sección (o 'rango' en el lenguaje HTTP) de una página web, siempre que el servidor admita este comportamiento.
El range
cabecera
Cuando se envía una solicitud a un servidor, los parámetros de la petición se dan en diferentes cabeceras. Uno de estos es el encabezado Range
, definido en section 14.35 of RFC2616 (la especificación que define HTTP/1.1). Este encabezado le permite hacer cosas tales como recuperar todos los datos a partir del octeto número 10.000 o los datos entre los bytes 1.000 y 1.500.
apoyo servidor
No existe ningún requisito para un servidor para apoyar la recuperación de rango. Algunos servidores devolverán el encabezado Accept-Ranges
(section 14.5 of RFC2616) junto con una respuesta para informar si admiten rangos o no. Esto podría verificarse utilizando una solicitud HEAD. Sin embargo, no hay una necesidad particular de hacer esto; si un servidor no admite rangos, devolverá toda la página y luego podremos extraer la porción de datos deseada en Python como antes.
Verificando una gama se devuelve
Si el servidor devuelve un rango, se debe enviar la cabecera Content-Range
(section 14.16 of RFC2616) junto con la respuesta. Si esto está presente en los encabezados de la respuesta, sabemos que se devolvió un rango; si no está presente, se devolvió la página completa.
Ejecución con urllib2
urllib2
nos permite añadir cabeceras a una solicitud, lo que nos permite pedir al servidor para un rango en lugar de toda la página. El siguiente script toma una URL, una posición de inicio y (opcionalmente) una longitud en la línea de comando, e intenta recuperar la sección dada de la página.
import sys
import urllib2
# Check command line arguments.
if len(sys.argv) < 3:
sys.stderr.write("Usage: %s url start [length]\n" % sys.argv[0])
sys.exit(1)
# Create a request for the given URL.
request = urllib2.Request(sys.argv[1])
# Add the header to specify the range to download.
if len(sys.argv) > 3:
start, length = map(int, sys.argv[2:])
request.add_header("range", "bytes=%d-%d" % (start, start + length - 1))
else:
request.add_header("range", "bytes=%s-" % sys.argv[2])
# Try to get the response. This will raise a urllib2.URLError if there is a
# problem (e.g., invalid URL).
response = urllib2.urlopen(request)
# If a content-range header is present, partial retrieval worked.
if "content-range" in response.headers:
print "Partial retrieval successful."
# The header contains the string 'bytes', followed by a space, then the
# range in the format 'start-end', followed by a slash and then the total
# size of the page (or an asterix if the total size is unknown). Lets get
# the range and total size from this.
range, total = response.headers['content-range'].split(' ')[-1].split('/')
# Print a message giving the range information.
if total == '*':
print "Bytes %s of an unknown total were retrieved." % range
else:
print "Bytes %s of a total of %s were retrieved." % (range, total)
# No header, so partial retrieval was unsuccessful.
else:
print "Unable to use partial retrieval."
# And for good measure, lets check how much data we downloaded.
data = response.read()
print "Retrieved data size: %d bytes" % len(data)
Usando esto, puedo recuperar los últimos 2.000 bytes de la página Python:
[email protected]:~$ python retrieverange.py http://www.python.org/ 17387
Partial retrieval successful.
Bytes 17387-19386 of a total of 19387 were retrieved.
Retrieved data size: 2000 bytes
o 400 bytes desde el medio de la página de inicio:
[email protected]:~$ python retrieverange.py http://www.python.org/ 6000 400
Partial retrieval successful.
Bytes 6000-6399 of a total of 19387 were retrieved.
Retrieved data size: 400 bytes
Sin embargo, el Google Página de inicio no admite rangos:
[email protected]:~$ python retrieverange.py http://www.google.com/ 1000 500
Unable to use partial retrieval.
Retrieved data size: 9621 bytes
En este caso, sería necesario extraer los datos de interés en Python antes de cualquier procesamiento posterior.
Puede funcionar mejor simplemente escribir los datos en un archivo (o incluso en una cadena, usando StringIO), y buscar en ese archivo (o cadena).
Digamos de una respuesta del 1MB la primera 900KB son inútiles para mí, así que es una oportunidad para acelerar el proceso y no para descargarlos. –
Ver
Python seek on remote file using HTTP
La solución basada en el apoyo gama HTTP tal como se define en el RFC 2616.
¿No está limitado el alcance con la compatibilidad del servidor? –
No encontré ninguna implementación existente de una interfaz de tipo archivo con seek() para las URL HTTP, así que lancé mi propia versión simple: https://github.com/valgur/pyhttpio. Depende de urllib.request
, pero probablemente podría modificarse fácilmente para usar requests
, si es necesario.
El código completo:
import cgi
import time
import urllib.request
from io import IOBase
from sys import stderr
class SeekableHTTPFile(IOBase):
def __init__(self, url, name=None, repeat_time=-1, debug=False):
"""Allow a file accessible via HTTP to be used like a local file by utilities
that use `seek()` to read arbitrary parts of the file, such as `ZipFile`.
Seeking is done via the 'range: bytes=xx-yy' HTTP header.
Parameters
----------
url : str
A HTTP or HTTPS URL
name : str, optional
The filename of the file.
Will be filled from the Content-Disposition header if not provided.
repeat_time : int, optional
In case of HTTP errors wait `repeat_time` seconds before trying again.
Negative value or `None` disables retrying and simply passes on the exception (the default).
"""
super().__init__()
self.url = url
self.name = name
self.repeat_time = repeat_time
self.debug = debug
self._pos = 0
self._seekable = True
with self._urlopen() as f:
if self.debug:
print(f.getheaders())
self.content_length = int(f.getheader("Content-Length", -1))
if self.content_length < 0:
self._seekable = False
if f.getheader("Accept-Ranges", "none").lower() != "bytes":
self._seekable = False
if name is None:
header = f.getheader("Content-Disposition")
if header:
value, params = cgi.parse_header(header)
self.name = params["filename"]
def seek(self, offset, whence=0):
if not self.seekable():
raise OSError
if whence == 0:
self._pos = 0
elif whence == 1:
pass
elif whence == 2:
self._pos = self.content_length
self._pos += offset
return self._pos
def seekable(self, *args, **kwargs):
return self._seekable
def readable(self, *args, **kwargs):
return not self.closed
def writable(self, *args, **kwargs):
return False
def read(self, amt=-1):
if self._pos >= self.content_length:
return b""
if amt < 0:
end = self.content_length - 1
else:
end = min(self._pos + amt - 1, self.content_length - 1)
byte_range = (self._pos, end)
self._pos = end + 1
with self._urlopen(byte_range) as f:
return f.read()
def readall(self):
return self.read(-1)
def tell(self):
return self._pos
def __getattribute__(self, item):
attr = object.__getattribute__(self, item)
if not object.__getattribute__(self, "debug"):
return attr
if hasattr(attr, '__call__'):
def trace(*args, **kwargs):
a = ", ".join(map(str, args))
if kwargs:
a += ", ".join(["{}={}".format(k, v) for k, v in kwargs.items()])
print("Calling: {}({})".format(item, a))
return attr(*args, **kwargs)
return trace
else:
return attr
def _urlopen(self, byte_range=None):
header = {}
if byte_range:
header = {"range": "bytes={}-{}".format(*byte_range)}
while True:
try:
r = urllib.request.Request(self.url, headers=header)
return urllib.request.urlopen(r)
except urllib.error.HTTPError as e:
if self.repeat_time is None or self.repeat_time < 0:
raise
print("Server responded with " + str(e), file=stderr)
print("Sleeping for {} seconds before trying again".format(self.repeat_time), file=stderr)
time.sleep(self.repeat_time)
un potencial de uso de ejemplo:
url = "https://www.python.org/ftp/python/3.5.0/python-3.5.0-embed-amd64.zip"
f = SeekableHTTPFile(url, debug=True)
zf = ZipFile(f)
zf.printdir()
zf.extract("python.exe")
Editar: En realidad, hay una casi idénticas, aunque un poco más mínima, la aplicación en esta respuesta: https://stackoverflow.com/a/7852229/2997179
- 1. Algunas preguntas sobre escribir en la corriente de respuesta ASP.NET
- 2. Obteniendo y atrapando la respuesta HTTP usando Mechanize en Python
- 3. cómo python http solicitud y respuesta funciona
- 4. WebException al leer la corriente de respuesta de WebException
- 5. Descomprimir la respuesta GZIPed HTTP en Java
- 6. Python buscar en un archivo remoto usando HTTP
- 7. Modificar encabezados de respuesta HTTP en Firefox
- 8. Descomprimir la respuesta http comprimida gzip
- 9. ¿Cómo puedo hacer una solicitud http sin obtener una respuesta http en Python?
- 10. urllib2 no recupera toda la respuesta HTTP
- 11. Obtenga el tamaño de la respuesta HTTP en Java
- 12. ¿Cómo enviar correctamente la respuesta HTTP con Python usando solo la biblioteca de socket?
- 13. Generar una respuesta HTTP en Ruby
- 14. Respuesta HTTP POST a WebView en Android
- 15. Obteniendo la respuesta de la solicitud http sin contenido-longitud?
- 16. código de respuesta HTTP después de redirigir
- 17. analizar encabezado de respuesta http de wget
- 18. Transmitir un archivo a la respuesta HTTP en Pylons
- 19. Transmisión de respuesta HTTP, rubor al navegador
- 20. ¿Cómo eliminar un encabezado de respuesta HTTP?
- 21. DotNetZip Guardar en la corriente
- 22. Estilo WPF basado en la corriente
- 23. render_to_response con cabeceras de respuesta HTTP
- 24. Cómo depurar los encabezados de respuesta http de una llamada HTTP
- 25. La respuesta de falla de HTTP sobrescribe el cuerpo de respuesta para clientes no locales
- 26. node.js escribir la respuesta http a la secuencia
- 27. Cómo leer el flujo de respuesta antes de que se complete la respuesta Http
- 28. ¿Cómo veo la respuesta HTTP a una solicitud de ActiveResource?
- 29. ¿Cómo eliminar encabezados HTTP de la respuesta CURL?
- 30. Cómo decodificar los datos comprimidos gzip devueltos en una respuesta HTTP en python?
¿Qué quiere decir con 'buscar en la secuencia de respuesta http'? – phooji
Una vez usé C# y la implementación de lo que hablo fue así: 'WebClient.OpenRead(). Seek()'. –
Un objeto contenedor simple que puede dar esta funcionalidad usando la cabecera gama http: http://stackoverflow.com/questions/7829311/is-there-a-library-for-retrieving-a-file-from-a-remote- zip/7852229 # 7852229 – retracile