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
StringIOobjeto. 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
unicodeobjetos constrobjetos que no son decodificables por la codificación predeterminada del sistema (que es típicamenteascii).from io import StringIOA 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 linePython 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í
filehay 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:
StringIOmó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.urlopenfunció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 ... aslugar dezipfile = ...según los Docs ..namelist()para recorrer todos los archivos en el zip e imprimir su contenido.ZipFileobjeto a lawithdeclaració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
tempfilemódulo ( http://docs.python.org/library/tempfile.html ) tiene exactamente lo siguiente:o si es vago y tiene un tmpfs montado
/tmpen 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_stringfuente
Utilice el
zipfilemódulo. Para extraer un archivo de una URL, deberá envolver el resultado de unaurlopenllamada en unBytesIOobjeto. Esto se debe a que el resultado de una solicitud web devuelta porurlopenno 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 paseZipFiledirectamente 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
openguardar el archivo en modo binario ('rb') , no como texto o obtendrá unzipfile.BadZipFile: File is not a zip fileerror.Es una buena práctica usar todas estas cosas como administradores de contexto con la
withdeclaración, para que se cierren correctamente.fuente