python - con - Descargar y descomprimir un archivo.zip sin escribir en el disco
zipfile python (8)
A continuación se muestra un fragmento de código que utilicé para obtener un archivo csv comprimido, eche un vistazo:
Python 2 :
from StringIO import StringIO
from zipfile import ZipFile
from urllib import urlopen
resp = urlopen("http://www.test.com/file.zip")
zipfile = ZipFile(StringIO(resp.read()))
for line in zipfile.open(file).readlines():
print line
Python 3 :
from io import BytesIO
from zipfile import ZipFile
from urllib.request import urlopen
# or: requests.get(url).content
resp = urlopen("http://www.test.com/file.zip")
zipfile = ZipFile(BytesIO(resp.read()))
for line in zipfile.open(file).readlines():
print(line.decode(''utf-8''))
Aquí el file
es una cadena. Para obtener la cadena real que desea pasar, puede usar zipfile.namelist()
. Por ejemplo,
resp = urlopen(''http://mlg.ucd.ie/files/datasets/bbc.zip'')
zipfile = ZipFile(BytesIO(resp.read()))
zipfile.namelist()
# [''bbc.classes'', ''bbc.docs'', ''bbc.mtx'', ''bbc.terms'']
Logré hacer funcionar mi primer script python, que descarga una lista de archivos .ZIP de una URL y luego procede a extraer los archivos ZIP y los escribe en el disco.
Ahora estoy perdido para lograr el siguiente paso.
Mi objetivo principal es descargar y extraer el archivo zip y pasar los contenidos (datos CSV) a través de una transmisión TCP. Preferiría no escribir ninguno de los archivos zip o extraídos en el disco si pudiera salirse con la suya.
Aquí está mi script actual que funciona, pero desafortunadamente tiene que escribir los archivos en el disco.
import urllib, urllister
import zipfile
import urllib2
import os
import time
import pickle
# check for extraction directories existence
if not os.path.isdir(''downloaded''):
os.makedirs(''downloaded'')
if not os.path.isdir(''extracted''):
os.makedirs(''extracted'')
# open logfile for downloaded data and save to local variable
if os.path.isfile(''downloaded.pickle''):
downloadedLog = pickle.load(open(''downloaded.pickle''))
else:
downloadedLog = {''key'':''value''}
# remove entries older than 5 days (to maintain speed)
# path of zip files
zipFileURL = "http://www.thewebserver.com/that/contains/a/directory/of/zip/files"
# retrieve list of URLs from the webservers
usock = urllib.urlopen(zipFileURL)
parser = urllister.URLLister()
parser.feed(usock.read())
usock.close()
parser.close()
# only parse urls
for url in parser.urls:
if "PUBLIC_P5MIN" in url:
# download the file
downloadURL = zipFileURL + url
outputFilename = "downloaded/" + url
# check if file already exists on disk
if url in downloadedLog or os.path.isfile(outputFilename):
print "Skipping " + downloadURL
continue
print "Downloading ",downloadURL
response = urllib2.urlopen(downloadURL)
zippedData = response.read()
# save data to disk
print "Saving to ",outputFilename
output = open(outputFilename,''wb'')
output.write(zippedData)
output.close()
# extract the data
zfobj = zipfile.ZipFile(outputFilename)
for name in zfobj.namelist():
uncompressed = zfobj.read(name)
# save uncompressed data to disk
outputFilename = "extracted/" + name
print "Saving extracted file to ",outputFilename
output = open(outputFilename,''wb'')
output.write(uncompressed)
output.close()
# send data via tcp stream
# file successfully downloaded and extracted store into local log and filesystem log
downloadedLog[url] = time.time();
pickle.dump(downloadedLog, open(''downloaded.pickle'', "wb" ))
Agregando a las otras respuestas usando solicitudes :
# download from web
import requests
url = ''http://mlg.ucd.ie/files/datasets/bbc.zip''
content = requests.get(url)
# unzip the content
from io import BytesIO
from zipfile import ZipFile
f = ZipFile(BytesIO(content.content))
print(f.namelist())
# outputs [''bbc.classes'', ''bbc.docs'', ''bbc.mtx'', ''bbc.terms'']
Use la ayuda (f) para obtener más detalles sobre las funciones de, por ejemplo, extractall (), que extrae el contenido en un archivo comprimido con el que luego se puede abrir .
El ejemplo de Vishal, por grande que sea, confunde cuando se trata del nombre del archivo, y no veo el mérito de redefinir ''zipfile''.
Aquí está mi ejemplo que descarga un archivo zip que contiene algunos archivos, uno de los cuales es un archivo csv que posteriormente leo en un DataFrame de pandas:
from StringIO import StringIO
from zipfile import ZipFile
from urllib import urlopen
import pandas
url = urlopen("https://www.federalreserve.gov/apps/mdrm/pdf/MDRM.zip")
zf = ZipFile(StringIO(url.read()))
for item in zf.namelist():
print("File in zip: "+ item)
# find the first matching csv file in the zip:
match = [s for s in zf.namelist() if ".csv" in s][0]
# the first line of the file contains a string - that line shall de ignored, hence skiprows
df = pandas.read_csv(zf.open(match), low_memory=False, skiprows=[0])
(Nota, yo uso Python 2.7.13)
Me gustaría agregar mi respuesta de Python3 para que esté completa:
from io import BytesIO
from zipfile import ZipFile
import requests
def get_zip(file_url):
url = requests.get(file_url)
zipfile = ZipFile(BytesIO(url.content))
zip_names = zipfile.namelist()
if len(zip_names) == 1:
file_name = zip_names.pop()
extracted_file = zipfile.open(file_name)
return extracted_file
Me gustaría ofrecer una versión actualizada de Python 3 de la excelente respuesta de Vishal, que estaba usando Python 2, junto con alguna explicación de las adaptaciones / cambios, que ya se han mencionado.
from io import BytesIO
from zipfile import ZipFile
import urllib.request
url = urllib.request.urlopen("http://www.unece.org/fileadmin/DAM/cefact/locode/loc162txt.zip")
with ZipFile(BytesIO(url.read())) as my_zip_file:
for contained_file in my_zip_file.namelist():
# with open(("unzipped_and_read_" + contained_file + ".file"), "wb") as output:
for line in my_zip_file.open(contained_file).readlines():
print(line)
# output.write(line)
Cambios necesarios:
- No hay
StringIO
en Python 3. En su lugar, utilizoio
, y desde allí importoBytesIO
, porqueBytesIO
un bytestream - Docs , también este hilo . - urlopen:
- "La función legacy urllib.urlopen de Python 2.6 y anteriores se ha descontinuado; urllib.request.urlopen () corresponde al antiguo urllib2.urlopen.", Docs .
- import urllib.request:
Nota:
- En Python 3, las líneas de salida impresas se verán así:
b''some text''
. Esto es esperado, ya que no son cadenas, recuerde, estamos leyendo una copia de prueba. Eche un vistazo a la excelente respuesta de Dan04 .
Algunos cambios menores que hice:
- Uso
with ... as
lugar dezipfile = ...
según los Documentos . - La secuencia de comandos ahora usa
namelist()
para recorrer todos los archivos en el zip e imprimir sus contenidos. -
ZipFile
la creación del objetoZipFile
a la declaración con, aunque no estoy seguro si eso es mejor. - Agregué (y comenté) una opción para escribir el bytestream en el archivo (por archivo en el zip), en respuesta al comentario de NumenorForLife; agrega
"unzipped_and_read_"
al principio del nombre de archivo y una extensión".file"
(prefiero no usar".txt"
para archivos con cadenas de bytes). La sangría del código, por supuesto, deberá ajustarse si desea usarlo.- Necesitamos tener cuidado aquí, porque tenemos una cadena de bytes, usamos el modo binario, por lo que
"wb"
; Tengo la sensación de que escribir binarios abre una lata de gusanos de todos modos ...
- Necesitamos tener cuidado aquí, porque tenemos una cadena de bytes, usamos el modo binario, por lo que
- Estoy usando un archivo de ejemplo, el archivo de texto UN / LOCODE :
Lo que no hice:
- NumenorForLife preguntó por guardar el archivo zip en el disco. No estoy seguro de lo que quiso decir al descargar el archivo comprimido. Esa es una tarea diferente; ver la excelente respuesta de Oleh Prypin .
Aquí hay una manera:
import urllib.request
import shutil
with urllib.request.urlopen("http://www.unece.org/fileadmin/DAM/cefact/locode/2015-2_UNLOCODE_SecretariatNotes.pdf") as response, open("downloaded_file.pdf", ''w'') as out_file:
shutil.copyfileobj(response, out_file)
Mi sugerencia sería usar un objeto StringIO
. Emulan archivos, pero residen en la memoria. Entonces podrías hacer algo como esto:
# get_zip_data() gets a zip archive containing ''foo.txt'', reading ''hey, foo''
from StringIO import StringIO
zipdata = StringIO()
zipdata.write(get_zip_data())
myzipfile = zipfile.ZipFile(zipdata)
foofile = myzipfile.open(''foo.txt'')
print foofile.read()
# output: "hey, foo"
O más simplemente (disculpas a Vishal):
myzipfile = zipfile.ZipFile(StringIO(get_zip_data()))
for name in myzipfile.namelist():
[ ... ]
En Python 3 usa BytesIO en lugar de StringIO.
No era obvio en la respuesta de Vishal cuál se suponía que era el nombre del archivo en los casos en que no hay ningún archivo en el disco. He modificado su respuesta para que funcione sin modificaciones para la mayoría de las necesidades.
from StringIO import StringIO
from zipfile import ZipFile
from urllib import urlopen
def unzip_string(zipped_string):
unzipped_string = ''''
zipfile = ZipFile(StringIO(zipped_string))
for name in zipfile.namelist():
unzipped_string += zipfile.open(name).read()
return unzipped_string
escribir en un archivo temporal que reside en la RAM
resulta que el módulo tempfile
( http://docs.python.org/library/tempfile.html ) tiene exactamente lo siguiente:
tempfile.SpooledTemporaryFile ([max_size = 0 [, mode = ''w + b'' [, bufsize = -1 [, sufijo = '''' [, prefijo = ''tmp'' [, dir = None]]]]])
Esta función funciona exactamente como TemporaryFile (), excepto que los datos se almacenan en la memoria hasta que el tamaño del archivo exceda max_size, o hasta que se llame al método fileno (), en cuyo punto los contenidos se escriben en el disco y la operación continúa como con TemporaryFile ().
El archivo resultante tiene un método adicional, rollover (), que hace que el archivo se transfiera a un archivo en disco, independientemente de su tamaño.
El objeto devuelto es un objeto similar a un archivo cuyo atributo _file es un objeto StringIO o un objeto de archivo verdadero, dependiendo de si se ha llamado al rollover (). Este objeto similar a un archivo se puede usar en una declaración con, como un archivo normal.
Nuevo en la versión 2.6.
o si eres perezoso y tienes un tmpfs-mounted /tmp
en Linux, puedes hacer un archivo allí, pero debes eliminarlo tú mismo y tratar con los nombres