Мне удалось заставить работать мой первый скрипт Python, который загружает список файлов .ZIP из URL-адреса, а затем переходит к извлечению файлов ZIP и записывает их на диск.
Сейчас я не могу сделать следующий шаг.
Моя основная цель - загрузить и извлечь zip-файл и передать его содержимое (данные CSV) через поток TCP. Я бы предпочел не записывать на диск какие-либо zip-архивы или извлеченные файлы, если бы это сошло мне с рук.
Вот мой текущий сценарий, который работает, но, к сожалению, должен записывать файлы на диск.
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" ))
Ответы:
Я предлагаю использовать
StringIO
объект. Они имитируют файлы, но находятся в памяти. Итак, вы могли бы сделать что-то вроде этого:# 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"
Или проще (извинения перед Вишалом):
myzipfile = zipfile.ZipFile(StringIO(get_zip_data())) for name in myzipfile.namelist(): [ ... ]
В Python 3 используйте BytesIO вместо StringIO:
import zipfile from io import BytesIO filebytes = BytesIO(get_zip_data()) myzipfile = zipfile.ZipFile(filebytes) for name in myzipfile.namelist(): [ ... ]
источник
unicode
объекты сstr
объектами, которые не могут быть декодированы системной кодировкой по умолчанию (которая обычно таковаascii
).from io import StringIO
Ниже приведен фрагмент кода, который я использовал для получения заархивированного CSV-файла, пожалуйста, посмотрите:
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'))
Вот
file
строка. Чтобы получить фактическую строку, которую вы хотите передать, вы можете использоватьzipfile.namelist()
. Например,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']
источник
Я хотел бы предложить обновленную версию Python 3 отличного ответа Вишала, в котором использовался Python 2, вместе с некоторым объяснением адаптаций / изменений, которые, возможно, уже упоминались.
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)
Необходимые изменения:
StringIO
Python 3 нет модуля (он был перемещенio.StringIO
). Вместо этого я используюio.BytesIO
] 2 , потому что мы будем обрабатывать байтовый поток - Docs , а также этот поток .urllib.urlopen
функция из Python 2.6 и ранее была прекращена;urllib.request.urlopen()
соответствует старомуurllib2.urlopen
.», Документы и этот поток .Заметка:
b'some text'
. Это ожидается, поскольку они не являются строками - помните, мы читаем байтовый поток. Взгляните на отличный ответ Dan04 .Я сделал несколько небольших изменений:
with ... as
вместоzipfile = ...
согласно Документам ..namelist()
для циклического перебора всех файлов в zip-архиве и печати их содержимого.ZipFile
объекта вwith
оператор, хотя не уверен, что это лучше."unzipped_and_read_"
в начало имени файла и".file"
расширение (я предпочитаю не использовать".txt"
для файлов со строками байтов). Отступы в коде, конечно, нужно будет отрегулировать, если вы хотите его использовать."wb"
; У меня такое чувство, что написание двоичного кода в любом случае открывает банку червей ...Чего я не делал:
Вот способ:
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)
источник
записать во временный файл, который находится в ОЗУ
оказывается, что в
tempfile
модуле ( http://docs.python.org/library/tempfile.html ) есть как раз то, что нужно:или если вы ленивы и у вас установлен tmpfs
/tmp
в Linux, вы можете просто создать там файл, но вам придется удалить его самостоятельно и разобраться с именамиисточник
Я хотел бы добавить свой ответ Python3 для полноты:
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]
источник
Добавление к другим ответам с помощью запросов :
# 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']
Используйте help (f) для получения дополнительных сведений о функциях, например, extractall (), которая извлекает содержимое из zip-файла, которое позже можно использовать с open .
источник
with f.open(f.namelist()[0], 'r') as g: df = pd.read_csv(g)
Пример Вишала, каким бы великолепным он ни был, сбивает с толку, когда дело доходит до имени файла, и я не вижу достоинств переопределения «zipfile».
Вот мой пример, который загружает zip-архив, содержащий некоторые файлы, один из которых является файлом csv, который я впоследствии прочитал в 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])
(Обратите внимание, я использую Python 2.7.13)
Это точное решение, которое сработало для меня. Я просто немного подправил его для версии Python 3, удалив StringIO и добавив библиотеку ввода-вывода.
Версия 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])
источник
В ответе Вишала не было очевидно, каким должно быть имя файла в тех случаях, когда на диске нет файла. Я изменил его ответ, чтобы он работал без изменений для большинства нужд.
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
источник
Используйте
zipfile
модуль. Чтобы извлечь файл из URL-адреса, вам необходимо обернуть результатurlopen
вызова вBytesIO
объект. Это связано с тем, что результат веб-запроса, возвращенныйurlopen
не поддерживает поиск: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())
Если у вас уже есть файл, загруженный локально, вам не нужно
BytesIO
, просто откройте его в двоичном режиме и перейдитеZipFile
напрямую: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'))
Опять же, обратите внимание, что вы должны
open
использовать файл в двоичном ('rb'
) режиме , а не в виде текста, иначе вы получите сообщениеzipfile.BadZipFile: File is not a zip file
об ошибке.Хорошая практика - использовать все эти вещи в качестве менеджеров контекста с
with
оператором, чтобы они были закрыты должным образом.источник
Все эти ответы кажутся громоздкими и длинными. Используйте запросы для сокращения кода, например:
import requests, zipfile, io r = requests.get(zip_file_url) z = zipfile.ZipFile(io.BytesIO(r.content)) z.extractall("/path/to/directory")
источник