Me las arreglé para hacer funcionar mi primer script de 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 no puedo dar el siguiente paso.
Mi objetivo principal es descargar y extraer el archivo zip y pasar el contenido (datos CSV) a través de una secuencia TCP. Preferiría no escribir ninguno de los archivos zip o extraídos en el disco si pudiera salirme con la mía.
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" ))
Respuestas:
Mi sugerencia sería utilizar un
StringIO
objeto. 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' import zipfile 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 use BytesIO en lugar de StringIO:
import zipfile from io import BytesIO filebytes = BytesIO(get_zip_data()) myzipfile = zipfile.ZipFile(filebytes) for name in myzipfile.namelist(): [ ... ]
fuente
unicode
objetos constr
objetos que no son decodificables por la codificación predeterminada del sistema (que es típicamenteascii
).from io import StringIO
A continuación se muestra un fragmento de código que utilicé para recuperar el archivo csv comprimido, por favor, 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í
file
hay una cadena. Para obtener la cadena real que desea pasar, puede usarzipfile.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']
fuente
Me gustaría ofrecer una versión actualizada de Python 3 de la excelente respuesta de Vishal, que estaba usando Python 2, junto con una explicación de las adaptaciones / cambios, que puede que ya se hayan 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:
StringIO
módulo en Python 3 (se ha movido aio.StringIO
). En su lugar, usoio.BytesIO
] 2 , porque manejaremos un bytestream: Docs , también este hilo .urllib.urlopen
función heredada de Python 2.6 y versiones anteriores ha sido descontinuada;urllib.request.urlopen()
corresponde a la antiguaurllib2.urlopen
", Docs y este hilo .Nota:
b'some text'
. Es de esperar, ya que no son cadenas; recuerde, estamos leyendo un bytestream. Eche un vistazo a la excelente respuesta de Dan04 .Algunos cambios menores que hice:
with ... as
lugar dezipfile = ...
según los Docs ..namelist()
para recorrer todos los archivos en el zip e imprimir su contenido.ZipFile
objeto a lawith
declaración, aunque no estoy seguro si eso es mejor."unzipped_and_read_"
al comienzo del nombre del archivo y una".file"
extensión (prefiero no usar".txt"
para archivos con cadenas de bytes). La sangría del código, por supuesto, deberá ajustarse si desea utilizarlo."wb"
; Tengo la sensación de que escribir binario abre una lata de gusanos de todos modos ...Lo que no hice:
He aquí una forma:
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)
fuente
escribir en un archivo temporal que reside en la RAM
resulta que el
tempfile
módulo ( http://docs.python.org/library/tempfile.html ) tiene exactamente lo siguiente:o si es vago y tiene un tmpfs montado
/tmp
en Linux, puede simplemente crear un archivo allí, pero debe eliminarlo usted mismo y ocuparse de los nombresfuente
Me gustaría agregar mi respuesta de Python3 para completar:
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 return [zipfile.open(file_name) for file_name in zip_names]
fuente
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 help (f) para obtener más detalles de las funciones, por ejemplo, extractall () que extrae el contenido en un archivo zip que luego se puede usar con open .
fuente
with f.open(f.namelist()[0], 'r') as g: df = pd.read_csv(g)
El ejemplo de Vishal, por muy bueno 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 zip que contiene algunos archivos, uno de los cuales es un archivo csv que luego 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)
Esta es la solución exacta que funcionó para mí. Simplemente lo modifiqué un poco para la versión de Python 3 eliminando StringIO y agregando la biblioteca IO
Versión de Python 3
from io import BytesIO from zipfile import ZipFile import pandas import requests url = "https://www.nseindia.com/content/indices/mcwb_jun19.zip" content = requests.get(url) zf = ZipFile(BytesIO(content.content)) 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])
fuente
No era obvio en la respuesta de Vishal cuál se suponía que era el nombre del archivo en los casos en los que no hay ningún archivo en el disco. Modifiqué 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
fuente
Utilice el
zipfile
módulo. Para extraer un archivo de una URL, deberá envolver el resultado de unaurlopen
llamada en unBytesIO
objeto. Esto se debe a que el resultado de una solicitud web devuelta porurlopen
no admite la búsqueda:from urllib.request import urlopen from io import BytesIO from zipfile import ZipFile zip_url = 'http://example.com/my_file.zip' with urlopen(zip_url) as f: with BytesIO(f.read()) as b, ZipFile(b) as myzipfile: foofile = myzipfile.open('foo.txt') print(foofile.read())
Si ya tiene el archivo descargado localmente, no lo necesita
BytesIO
, simplemente ábralo en modo binario y paseZipFile
directamente a:from zipfile import ZipFile zip_filename = 'my_file.zip' with open(zip_filename, 'rb') as f: with ZipFile(f) as myzipfile: foofile = myzipfile.open('foo.txt') print(foofile.read().decode('utf-8'))
Nuevamente, tenga en cuenta que tiene que
open
guardar el archivo en modo binario ('rb'
) , no como texto o obtendrá unzipfile.BadZipFile: File is not a zip file
error.Es una buena práctica usar todas estas cosas como administradores de contexto con la
with
declaración, para que se cierren correctamente.fuente