Consegui fazer meu primeiro script Python funcionar, o qual baixa uma lista de arquivos .ZIP de uma URL e, em seguida, extrai os arquivos ZIP e os grava no disco.
Agora não consigo dar o próximo passo.
Meu objetivo principal é baixar e extrair o arquivo zip e passar o conteúdo (dados CSV) por meio de um fluxo TCP. Eu preferiria não gravar nenhum dos arquivos compactados ou extraídos no disco se eu pudesse me safar.
Aqui está meu script atual que funciona, mas infelizmente tem que gravar os arquivos no 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" ))
Respostas:
Minha sugestão seria usar um
StringIO
objeto. Eles emulam arquivos, mas residem na memória. Então você poderia fazer algo assim:# 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"
Ou mais simplesmente (desculpas a Vishal):
myzipfile = zipfile.ZipFile(StringIO(get_zip_data())) for name in myzipfile.namelist(): [ ... ]
No Python 3, use BytesIO em vez de StringIO:
import zipfile from io import BytesIO filebytes = BytesIO(get_zip_data()) myzipfile = zipfile.ZipFile(filebytes) for name in myzipfile.namelist(): [ ... ]
fonte
unicode
objetos comstr
objetos que não são decodificáveis pela codificação padrão do sistema (que normalmente éascii
).from io import StringIO
Abaixo está um snippet de código que usei para buscar o arquivo csv compactado, por favor, dê uma olhada:
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'))
Aqui
file
está uma string. Para obter a string real que deseja passar, você pode usarzipfile.namelist()
. Por exemplo,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']
fonte
Eu gostaria de oferecer uma versão atualizada do Python 3 da excelente resposta de Vishal, que estava usando Python 2, junto com algumas explicações sobre as adaptações / mudanças, que podem ter sido mencionadas.
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)
Mudanças necessárias:
StringIO
módulo no Python 3 (ele foi movido paraio.StringIO
). Em vez disso, eu usoio.BytesIO
] 2 , porque estaremos lidando com um bytestream - Docs , também este tópico .urllib.urlopen
função legada do Python 2.6 e anterior foi descontinuada;urllib.request.urlopen()
corresponde à antigaurllib2.urlopen
.", Docs e este tópico .Nota:
b'some text'
. Isso é esperado, pois não são strings - lembre-se, estamos lendo um bytestream. Dê uma olhada na excelente resposta de Dan04 .Algumas pequenas alterações que fiz:
with ... as
vez de dezipfile = ...
acordo com o Docs ..namelist()
para percorrer todos os arquivos no zip e imprimir seu conteúdo.ZipFile
objeto para awith
instrução, embora não tenha certeza se isso é melhor."unzipped_and_read_"
ao início do nome do arquivo e uma".file"
extensão (prefiro não usar".txt"
para arquivos com bytes). O recuo do código, é claro, precisa ser ajustado se você quiser usá-lo."wb"
; Tenho a sensação de que escrever binário abre uma lata de vermes de qualquer maneira ...O que eu não fiz:
Aqui está uma maneira:
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)
fonte
escrever em um arquivo temporário que reside na RAM
Acontece que o
tempfile
módulo ( http://docs.python.org/library/tempfile.html ) tem exatamente a coisa:ou se você for preguiçoso e tiver um tmpfs montado
/tmp
no Linux, você pode simplesmente fazer um arquivo lá, mas você mesmo tem que deletar e lidar com a nomenclaturafonte
Eu gostaria de adicionar minha resposta 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]
fonte
Somando-se a outras respostas usando solicitações :
# 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 obter mais detalhes de funções para, por exemplo, extractall () que extrai o conteúdo de um arquivo zip que mais tarde pode ser usado com open .
fonte
with f.open(f.namelist()[0], 'r') as g: df = pd.read_csv(g)
O exemplo de Vishal, por maior que seja, confunde quando se trata do nome do arquivo, e não vejo o mérito de redefinir 'zipfile'.
Aqui está meu exemplo que baixa um zip que contém alguns arquivos, um dos quais é um arquivo csv que posteriormente li em um DataFrame do 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, eu uso Python 2.7.13)
Esta é a solução exata que funcionou para mim. Acabei de ajustar um pouco para a versão Python 3 removendo StringIO e adicionando biblioteca IO
Versão 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])
fonte
Não era óbvio na resposta de Vishal qual deveria ser o nome do arquivo nos casos em que não havia arquivo no disco. Modifiquei sua resposta para funcionar sem modificações para a maioria das necessidades.
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
fonte
Use o
zipfile
módulo. Para extrair um arquivo de um URL, você precisará envolver o resultado de umaurlopen
chamada em umBytesIO
objeto. Isso ocorre porque o resultado de uma solicitação da web retornada porurlopen
não suporta a busca: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())
Se você já fez o download do arquivo localmente, não é necessário
BytesIO
, basta abri-lo em modo binário e passarZipFile
diretamente para :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'))
Mais uma vez, observe que você precisa
open
do arquivo no modo binary ('rb'
) , não como texto ou você obterá umzipfile.BadZipFile: File is not a zip file
erro.É uma boa prática usar todas essas coisas como gerenciadores de contexto com a
with
instrução, para que sejam fechadas corretamente.fonte
Todas essas respostas parecem volumosas e longas. Use solicitações para encurtar o código, por exemplo:
import requests, zipfile, io r = requests.get(zip_file_url) z = zipfile.ZipFile(io.BytesIO(r.content)) z.extractall("/path/to/directory")
fonte