Udało mi się uruchomić mój pierwszy skrypt Pythona, który pobiera listę plików ZIP z adresu URL, a następnie przystępuje do wyodrębniania plików ZIP i zapisuje je na dysku.
Nie mogę teraz osiągnąć następnego kroku.
Moim głównym celem jest pobranie i wyodrębnienie pliku zip i przekazanie zawartości (dane CSV) przez strumień TCP. Wolałbym nie zapisywać żadnych spakowanych lub wyodrębnionych plików na dysk, gdybym mógł sobie z tym poradzić.
Oto mój aktualny skrypt, który działa, ale niestety musi zapisać pliki na dysku.
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" ))
Odpowiedzi:
Moją sugestią byłoby użycie
StringIO
przedmiotu. Emulują pliki, ale znajdują się w pamięci. Możesz więc zrobić coś takiego:# 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"
Lub prościej (przepraszam Vishal):
myzipfile = zipfile.ZipFile(StringIO(get_zip_data())) for name in myzipfile.namelist(): [ ... ]
W Pythonie 3 użyj BytesIO zamiast StringIO:
import zipfile from io import BytesIO filebytes = BytesIO(get_zip_data()) myzipfile = zipfile.ZipFile(filebytes) for name in myzipfile.namelist(): [ ... ]
źródło
unicode
obiekty zstr
obiektami, których nie można dekodować za pomocą domyślnego kodowania systemu (co jest typoweascii
).from io import StringIO
Poniżej znajduje się fragment kodu, którego użyłem do pobrania spakowanego pliku CSV, spójrz:
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'))
Oto
file
sznurek. Aby uzyskać rzeczywisty ciąg, który chcesz przekazać, możesz użyćzipfile.namelist()
. Na przykład,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']
źródło
Chciałbym zaoferować zaktualizowaną wersję doskonałej odpowiedzi Vishal w Pythonie 3, która korzystała z Pythona 2, wraz z wyjaśnieniem adaptacji / zmian, o których być może już wspomniano.
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)
Potrzebne zmiany:
StringIO
Pythonie 3 nie ma modułu (został przeniesionyio.StringIO
). Zamiast tego używamio.BytesIO
] 2 , ponieważ będziemy obsługiwać bytestream - Docs , także ten wątek .urllib.urlopen
funkcja z Pythona 2.6 i wcześniejszych została wycofana;urllib.request.urlopen()
odpowiada starejurllib2.urlopen
.”, Docs i ten wątek .Uwaga:
b'some text'
. Jest to oczekiwane, ponieważ nie są to ciągi znaków - pamiętaj, czytamy bajtestream. Spójrz na doskonałą odpowiedź Dan04 .Kilka drobnych zmian, które wprowadziłem:
with ... as
zamiastzipfile = ...
zgodnie z Docs ..namelist()
do przeglądania wszystkich plików w archiwum zip i drukowania ich zawartości.ZipFile
obiektu dowith
instrukcji, chociaż nie jestem pewien, czy to lepiej."unzipped_and_read_"
na początku nazwy pliku i".file"
rozszerzenia (wolę nie używać".txt"
dla plików z bajtestami). Wcięcie kodu będzie oczywiście wymagało dostosowania, jeśli chcesz go użyć."wb"
; Mam wrażenie, że pisanie plików binarnych i tak otwiera puszkę robaków ...Czego nie zrobiłem:
Oto sposób:
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)
źródło
zapisz do pliku tymczasowego, który znajduje się w pamięci RAM
okazuje się, że
tempfile
moduł ( http://docs.python.org/library/tempfile.html ) ma właśnie to:lub jeśli jesteś leniwy i masz zamontowany tmpfs
/tmp
w systemie Linux, możesz po prostu utworzyć tam plik, ale musisz go usunąć samodzielnie i zająć się nazewnictwemźródło
Chciałbym dodać moją odpowiedź w Pythonie3 dla kompletności:
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]
źródło
Dodając do innych odpowiedzi za pomocą zapytań :
# 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']
Skorzystaj z pomocy (f), aby uzyskać więcej szczegółów na temat funkcji, np. Extractall (), która wyodrębnia zawartość w pliku zip, którego później można używać z open .
źródło
with f.open(f.namelist()[0], 'r') as g: df = pd.read_csv(g)
Przykład Vishala, jakkolwiek świetny, wprowadza zamieszanie, jeśli chodzi o nazwę pliku, i nie widzę korzyści z redefinicji „zipfile”.
Oto mój przykład, który pobiera plik zip zawierający niektóre pliki, z których jeden jest plikiem csv, który następnie wczytuję do pandy 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])
(Uwaga, używam Pythona 2.7.13)
To jest dokładne rozwiązanie, które u mnie zadziałało. Po prostu poprawiłem go trochę dla wersji Python 3, usuwając StringIO i dodając bibliotekę IO
Wersja Pythona 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])
źródło
W odpowiedzi Vishala nie było oczywiste, jaka powinna być nazwa pliku w przypadkach, gdy na dysku nie ma pliku. Zmodyfikowałem jego odpowiedź, aby działała bez modyfikacji dla większości potrzeb.
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
źródło
Użyj
zipfile
modułu. Aby wyodrębnić plik z adresu URL, musisz zawrzeć wynikurlopen
wywołania wBytesIO
obiekcie. Dzieje się tak, ponieważ wynik żądania internetowego zwróconego przezurlopen
nie obsługuje wyszukiwania: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())
Jeśli masz już plik pobrany lokalnie, nie potrzebujesz
BytesIO
, po prostu otwórz go w trybie binarnym i przejdźZipFile
bezpośrednio do :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'))
Ponownie zwróć uwagę, że
open
plik musi być w trybie binary ('rb'
) , a nie jako tekst, w przeciwnym razie pojawi sięzipfile.BadZipFile: File is not a zip file
błąd.Dobrą praktyką jest używanie wszystkich tych rzeczy jako menedżerów kontekstu z
with
oświadczeniem, aby zostały poprawnie zamknięte.źródło