J'ai réussi à faire fonctionner mon premier script python) qui télécharge une liste de fichiers .Zip à partir d'une URL, puis procède à l'extraction des fichiers Zip et les écrit sur le disque.
Je suis maintenant perdu pour passer à l'étape suivante.
Mon objectif principal est de télécharger et d'extraire le fichier Zip et de passer le contenu (données CSV) via un flux TCP. Je préférerais ne pas réellement écrire l'un des fichiers Zip ou extraits sur le disque si Je pourrais m'en tirer.
Voici mon script actuel qui fonctionne mais doit malheureusement écrire les fichiers sur le disque.
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" ))
Ma suggestion serait d'utiliser un objet StringIO
. Ils émulent des fichiers, mais résident en mémoire. Vous pouvez donc faire quelque chose comme ceci:
# 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"
Ou plus simplement (excuses à Vishal):
myzipfile = zipfile.ZipFile(StringIO(get_Zip_data()))
for name in myzipfile.namelist():
[ ... ]
Dans Python 3 utilisez BytesIO au lieu de StringIO.
Ci-dessous un extrait de code que j'ai utilisé pour récupérer le fichier csv zippé, veuillez jeter un œil:
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:
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'))
Ici file
est une chaîne. Pour obtenir la chaîne réelle que vous souhaitez transmettre, vous pouvez utiliser zipfile.namelist()
. Par exemple,
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']
Je voudrais offrir une version mise à jour Python 3 de l'excellente réponse de Vishal, qui utilisait Python 2, avec une explication des adaptations/changements, qui ont peut-être déjà été mentionnés.
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)
Les changements nécessaires:
StringIO
dans Python 3. Au lieu de cela, j'utilise io
, et à partir de là j'importe BytesIO
, car nous allons gérer un bytestream - Docs , également ce fil .Remarque:
b'some text'
. Ceci est attendu, car ce ne sont pas des chaînes - rappelez-vous, nous lisons un flux-bytest. Have un coup d'oeil à excellente réponse de Dan04 .Quelques modifications mineures que j'ai apportées:
with ... as
Au lieu de zipfile = ...
Selon la documentation .namelist()
pour parcourir tous les fichiers du Zip et imprimer leur contenu.ZipFile
dans l'instruction with, mais je ne sais pas si c'est mieux."unzipped_and_read_"
au début du nom de fichier et une extension ".file"
(je préfère ne pas utiliser ".txt"
pour les fichiers avec des bytestrings). L'indentation du code devra, bien entendu, être ajustée si vous souhaitez l'utiliser. "wb"
; J'ai le sentiment que l'écriture binaire ouvre une boîte de vers de toute façon ...Ce que je n'ai pas fait:
Voici un moyen:
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)
écrire dans un fichier temporaire qui réside dans la RAM
il s'avère que le module tempfile
( http://docs.python.org/library/tempfile.html ) a juste ce qu'il faut:
tempfile.SpooledTemporaryFile ([max_size = 0 [ mode = 'w + b' [ bufsize = -1 [ suffix = = '' [ prefix = 'tmp' [ dir = None]]]]]]])]
Cette fonction fonctionne exactement comme TemporaryFile (), sauf que les données sont mises en file d'attente jusqu'à ce que la taille du fichier dépasse max_size, ou jusqu'à ce que la méthode fileno () du fichier soit appelée, moment auquel le contenu est écrit sur le disque et l'opération se déroule comme avec TemporaryFile ().
Le fichier résultant a une méthode supplémentaire, rollover (), qui fait que le fichier est transféré vers un fichier sur disque quelle que soit sa taille.
L'objet renvoyé est un objet de type fichier dont l'attribut _file est soit un objet StringIO, soit un véritable objet fichier, selon que rollover () a été appelé. Cet objet de type fichier peut être utilisé dans une instruction with, tout comme un fichier normal.
Nouveau dans la version 2.6.
ou si vous êtes paresseux et que vous avez un _ monté sur tmpfs /tmp
sous Linux, vous pouvez simplement y créer un fichier, mais vous devez le supprimer vous-même et gérer la dénomination
Je voudrais ajouter ma réponse Python3 pour être complète:
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]
Ajout aux autres réponses en utilisant requêtes:
# 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']
Utilisez aide (f) pour obtenir plus de détails sur les fonctions, par ex. extractall () qui extrait le contenu du fichier Zip qui peut ensuite être utilisé avec avec open.
Il n'était pas évident dans la réponse de Vishal quel devait être le nom du fichier dans les cas où il n'y a pas de fichier sur le disque. J'ai modifié sa réponse pour travailler sans modification pour la plupart des besoins.
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
L'exemple de Vishal, si grand soit-il, est déroutant en ce qui concerne le nom du fichier, et je ne vois pas l'intérêt de redéfinir le "fichier zip".
Voici mon exemple qui télécharge un Zip qui contient certains fichiers, dont l'un est un fichier csv que j'ai ensuite lu dans un pandas DataFrame:
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])
(Remarque, j'utilise Python 2.7.13)
C'est la solution exacte qui a fonctionné pour moi. Je l'ai juste légèrement modifié pour la version Python 3 en supprimant StringIO et en ajoutant IO bibliothèque
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])