Ich habe es geschafft, mein erstes python Skript zum Funktionieren zu bringen, das eine Liste von .Zip-Dateien von einer URL herunterlädt und dann fortfährt, die Zip-Dateien zu extrahieren und sie auf die Festplatte zu schreiben.
Ich bin jetzt ratlos, den nächsten Schritt zu erreichen.
Mein primäres Ziel ist das Herunterladen und Extrahieren der Zip-Datei und die Weitergabe der Inhalte (CSV-Daten) über einen TCP) - Stream. Ich würde es vorziehen, keine der Zip- oder extrahierten Dateien auf die Festplatte zu schreiben, wenn Ich könnte damit durchkommen.
Hier ist mein aktuelles Skript, das funktioniert, aber leider die Dateien auf die Festplatte schreiben muss.
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" ))
Mein Vorschlag wäre, ein StringIO
-Objekt zu verwenden. Sie emulieren Dateien, befinden sich jedoch im Speicher. Sie könnten also so etwas tun:
# 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"
Oder einfacher (entschuldigt sich bei Vishal):
myzipfile = zipfile.ZipFile(StringIO(get_Zip_data()))
for name in myzipfile.namelist():
[ ... ]
Verwenden Sie in Python 3 BytesIO anstelle von StringIO.
Unten ist ein Code-Snippet, mit dem ich eine gezippte CSV-Datei abgerufen habe:
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'))
Hier ist file
eine Zeichenkette. Um die tatsächliche Zeichenfolge abzurufen, die Sie übergeben möchten, können Sie zipfile.namelist()
verwenden. Zum Beispiel,
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']
Ich möchte eine aktualisierte Python 3-Version von Vishals exzellenter Antwort anbieten, die Python 2 verwendet, zusammen mit einer Erklärung der Anpassungen/Änderungen, was vielleicht schon erwähnt wurde.
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)
Notwendige Änderungen:
StringIO
in Python 3. Stattdessen verwende ich io
, und daraus importiere ich BytesIO
, weil wir a behandeln werden bytestream - Docs , auch dieser Thread .Hinweis:
b'some text'
Dies wird erwartet, da es sich nicht um Zeichenfolgen handelt - denken Sie daran, wir lesen einen Bytestream Ein Blick auf Dan04's ausgezeichnete Antwort .Ein paar kleine Änderungen, die ich vorgenommen habe:
with ... as
Anstelle von zipfile = ...
Gemäß der Dokumentation .namelist()
, um alle Dateien in der Zip-Datei zu durchlaufen und ihren Inhalt zu drucken.ZipFile
in die with-Anweisung verschoben, obwohl ich nicht sicher bin, ob das besser ist."unzipped_and_read_"
am Anfang des Dateinamens und eine ".file"
- Erweiterung hinzu (ich bevorzuge die Verwendung von ".txt"
für Dateien mit Bytestrings). Der Einzug des Codes muss natürlich angepasst werden, wenn Sie ihn verwenden möchten. "wb"
; Ich habe das Gefühl, dass das Schreiben von Binärdateien sowieso eine Dose Würmer öffnet ...Was ich nicht gemacht habe:
Hier ist ein Weg:
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)
in eine temporäre Datei schreiben, die sich im RAM befindet
es stellt sich heraus, dass das tempfile
-Modul ( http://docs.python.org/library/tempfile.html ) genau das Richtige hat:
tempfile.SpooledTemporaryFile ([max_size = 0 [ mode = 'w + b' [ bufsize = -1 [ suffix = '' [ prefix = 'tmp' [ dir = None]]]]])
Diese Funktion funktioniert genauso wie TemporaryFile (), mit der Ausnahme, dass Daten im Speicher gespoolt werden, bis die Dateigröße max_size überschreitet oder bis die fileno () -Methode der Datei aufgerufen wird. Anschließend wird der Inhalt auf die Festplatte geschrieben und der Vorgang wie bei TemporaryFile fortgesetzt ().
Die resultierende Datei verfügt über eine zusätzliche Methode, rollover (), mit der die Datei unabhängig von ihrer Größe in eine Datei auf der Festplatte verschoben wird.
Das zurückgegebene Objekt ist ein dateiähnliches Objekt, dessen _file-Attribut entweder ein StringIO-Objekt oder ein echtes Dateiobjekt ist, je nachdem, ob rollover () aufgerufen wurde. Dieses dateiähnliche Objekt kann wie eine normale Datei in einer with-Anweisung verwendet werden.
Neu in Version 2.6.
oder wenn du faul bist und ein tmpfs-mounted hast /tmp
Unter Linux können Sie dort einfach eine Datei erstellen, müssen diese jedoch selbst löschen und die Benennung vornehmen
Ich möchte der Vollständigkeit halber meine Python3-Antwort hinzufügen:
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]
Hinzufügen zu den anderen Antworten mit Anfragen:
# 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']
Verwenden Sie help (f), um weitere Funktionsdetails für z. extractall () extrahiert den Inhalt der Zip-Datei, der später mit with open verwendet werden kann.
In Vishals Antwort war nicht ersichtlich, wie der Dateiname lauten sollte, wenn sich keine Datei auf der Festplatte befindet. Ich habe seine Antwort so geändert, dass sie für die meisten Anforderungen unverändert bleibt.
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
Vishals Beispiel, so großartig es auch sein mag, ist verwirrend, wenn es um den Dateinamen geht, und ich sehe keinen Vorteil darin, 'zipfile' neu zu definieren.
In meinem Beispiel wird eine Zip-Datei heruntergeladen, die einige Dateien enthält. Eine davon ist eine CSV-Datei, die ich anschließend in einen 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])
(Beachte, ich benutze Python 2.7.13)
Dies ist die genaue Lösung, die für mich funktioniert hat. Ich habe es nur ein wenig für die Python 3-Version optimiert, indem ich StringIO entfernt und IO library hinzugefügt habe
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])