2010-08-20 25 views
6

Estoy intentando descargar un archivo zip utilizando este código:Python: Cómo descargar un archivo zip

o = urllib2.build_opener(urllib2.HTTPCookieProcessor()) 

#login 
p = urllib.urlencode({ usernameField: usernameVal, passField: passVal }) 
f = o.open(authUrl, p) 
data = f.read() 
print data 
f.close() 

#download file 
f = o.open(remoteFileUrl) 
localFile = open(localFile, "wb") 
localFile.write(f.read()) 
f.close() 

estoy recibiendo algunos datos binarios, pero el tamaño del archivo que he "descargado" es demasiado pequeño y no es un archivo zip válido. ¿No estoy recuperando el archivo zip correctamente? El encabezado de respuesta HTTP para f = o.open(remoteFileUrl) se muestra a continuación. No sé si se necesita un procesamiento especial para manejar esta respuesta:

HTTP/1.1 200 OK Servidor:
Apache-Coyote/1.1 Pragma: privada
Cache-Control: obligada revalidar
Expira : Martes, 31 de diciembre de 1997, 23:59:59 GMT
Disposición del contenido: en línea;
filename = "files.zip";
Content-Type: application/zip
Transfer-Encoding: fragmentada

Respuesta

10

f.read() no necesariamente leer todo el archivo, pero sólo un paquete de la misma (que podría ser todo el archivo si es pequeño, pero no será para un archivo grande).

Necesitas bucle sobre los paquetes de este tipo:

while 1: 
    packet = f.read() 
    if not packet: 
     break 
    localFile.write(packet) 
f.close() 

f.read() devuelve un paquete vacío para indicar que usted ha leído todo el archivo.

+2

Sería curioso cuando en la documentación que encontró esto –

+0

http://docs.python.org/library/urllib.html#urllib.urlopen: "se devuelve un objeto similar a un archivo" y luego http://docs.python.org/library/stdtypes.html#file .read – RichieHindle

+0

realmente solo un paquete? Revisé los documentos en el enlace que se muestra y no los veo en ningún lado. Dice que read() no lee hasta EOF. ¿Puedes explicarme mas? –

1

Si no te importa la lectura de la totalidad archivo zip en la memoria, de la manera más rápida de leer y escribir es de la siguiente manera:

data = f.readlines() 
with open(localFile,'wb') as output: 
    output.writelines(data) 

De lo contrario, a leer y escribir en trozos a medida que los recibe en la red, haga

with open(localFile, "wb") as output: 
    chunk = f.read() 
    while chunk: 
     output.write(chunk) 
     chunk = f.read() 

Esto es un poco menos prolijo, pero evita guardar todo el archivo en la memoria a la vez. Espero eso ayude.

0

Prueba esto:

#download file 
f = o.open(remoteFileUrl) 

response = "" 
while 1: 
    data = f.read() 
    if not data: 
     break 
    response += data 

with open(localFile, "wb") as local_file: 
    local_file.write(response) 
1

Aquí es una solución más robusta utilizando urllib2 para descargar el archivo en trozos e imprimir el estado de la descarga

import os 
import urllib2 
import math 

def downloadChunks(url): 
    """Helper to download large files 
     the only arg is a url 
     this file will go to a temp directory 
     the file will also be downloaded 
     in chunks and print out how much remains 
    """ 

    baseFile = os.path.basename(url) 

    #move the file to a more uniq path 
    os.umask(0002) 
    temp_path = "/tmp/" 
    try: 
     file = os.path.join(temp_path,baseFile) 

     req = urllib2.urlopen(url) 
     total_size = int(req.info().getheader('Content-Length').strip()) 
     downloaded = 0 
     CHUNK = 256 * 10240 
     with open(file, 'wb') as fp: 
      while True: 
       chunk = req.read(CHUNK) 
       downloaded += len(chunk) 
       print math.floor((downloaded/total_size) * 100) 
       if not chunk: break 
       fp.write(chunk) 
    except urllib2.HTTPError, e: 
     print "HTTP Error:",e.code , url 
     return False 
    except urllib2.URLError, e: 
     print "URL Error:",e.reason , url 
     return False 

    return file 
+0

Será robusto solo si maneja el caso donde no se envía encabezado 'Content-Length' IMO –

+0

Buen punto Xavier – Gourneau

Cuestiones relacionadas