Python 下载和解压缩.zip文件而不写入磁盘

Python 下载和解压缩.zip文件而不写入磁盘,python,unzip,Python,Unzip,我已经设法让我的第一个python脚本开始工作,它从URL下载一个.ZIP文件列表,然后继续提取ZIP文件并将其写入磁盘 我现在无法实现下一步 我的主要目标是下载和解压缩zip文件,并通过TCP流传递内容(CSV数据)。如果我能侥幸逃脱的话,我宁愿不将任何zip或解压缩文件写入磁盘 这是我当前的脚本,它可以工作,但不幸的是必须将文件写入磁盘 import urllib, urllister import zipfile import urllib2 import os import time i

我已经设法让我的第一个python脚本开始工作,它从URL下载一个.ZIP文件列表,然后继续提取ZIP文件并将其写入磁盘

我现在无法实现下一步

我的主要目标是下载和解压缩zip文件,并通过TCP流传递内容(CSV数据)。如果我能侥幸逃脱的话,我宁愿不将任何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" ))

写入驻留在RAM中的临时文件

事实证明,
tempfile
模块()具有以下功能:

tempfile.SpooledTemporaryFile([max_size=0[,, mode='w+b'[,bufsize=-1[,后缀=''[,, 前缀='tmp'[,dir=None]\\\\\\\]]

这个 函数的操作与 临时文件()没有,但数据除外 在内存中后台打印,直到文件 尺寸超过最大尺寸,或直到 在调用文件的fileno()方法 内容写在哪一点上 到磁盘,操作按 临时文件()

生成的文件还有一个附加属性 方法,rollover(),它会导致 要滚动到磁盘上文件的文件 不管它的大小

返回的对象是一个类似于 对象,其_file属性为 StringIO对象或真实文件 对象,具体取决于 已调用rollover()。这 类似文件的对象可用于具有 语句,就像普通文件一样

版本2.6中的新功能


或者,如果你很懒,在Linux上安装了一个tmpfs
/tmp
,你可以在那里创建一个文件,但你必须自己删除它并处理命名问题,我的建议是使用一个对象。它们模拟文件,但驻留在内存中。所以你可以这样做:

# 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"
或者更简单地说(向Vishal道歉):

在Python 3中,使用BytesIO代替StringIO:

import zipfile
from io import BytesIO

filebytes = BytesIO(get_zip_data())
myzipfile = zipfile.ZipFile(filebytes)
for name in myzipfile.namelist():
    [ ... ]

下面是我用来获取压缩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
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'))
 # 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']
Python 3

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
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'))
 # 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']
这里的
文件
是一个字符串。要获取要传递的实际字符串,可以使用
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']

在Vishal的回答中,在磁盘上没有文件的情况下,文件名应该是什么并不明显。我修改了他的答案,使之在大多数情况下都能正常工作

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

为了完整起见,我想添加我的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]

我想提供Vishal的优秀答案的更新版Python3,该答案使用Python2,并解释一些可能已经提到的调整/更改

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)
必要的改变:

  • Python3中没有模块(它已移动到
    io.StringIO
    )。相反,我使用的是
    io.BytesIO
    ],因为我们还将处理一个bytestream--
  • urlopen:
    • “Python2.6及更早版本中遗留的
      urllib.urlopen
      函数已停止使用;对应于旧的
      urllib2.urlopen
      ”,并且
注:

  • 在Python3中,打印的输出行如下所示:
    b'some text'
    。这是意料之中的,因为它们不是字符串——记住,我们正在读取ByTestStream。看一看
我做了一些小改动:

  • 我使用
    和。。。作为
    而不是zipfile=…根据
  • 脚本现在用于循环浏览zip中的所有文件并打印其内容
  • 我将对象的创建移到带有语句的
    中,尽管我不确定这是否更好
  • 为了响应NumenorForLife的评论,我添加了(并注释掉)一个将ByTestStream写入文件的选项(zip中的每个文件);它在文件名的开头添加了
    “解压”和“读取”
    ,并添加了
    “.file”
    扩展名(对于带有ByTestRing的文件,我不喜欢使用
    ”.txt“
    )。当然,如果您想使用代码,则需要调整代码的缩进。
    • 这里需要小心——因为我们有一个字节字符串,所以我们使用二进制模式,所以
      “wb”
      ;我有一种感觉,不管怎样,写二进制打开了一个蠕虫的罐子
  • 我正在使用一个示例文件,即:
我没有做的是:

  • NumenorForLife询问如何将zip保存到磁盘。我不知道他是什么意思下载zip文件?这是一个不同的任务;看
这里有一个方法:

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)

Vishal的例子,无论多么好,在文件名方面都会混淆,我不认为重新定义“zipfile”有什么好处

下面是我下载包含一些文件的zip的示例,其中一个是csv文件,我随后将其读入数据帧:

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)

这正是对我有效的解决方案。我只是通过删除StringIO并添加IO库对Python3版本做了一些调整

Python3版本
使用请求添加到其他答案上

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
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'))
 # 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']
使用帮助(f)获取更多函数的详细信息,例如extractall(),该函数提取zip文件中的内容,稍后可与一起使用open

使用该模块。要从URL提取文件,需要将调用结果包装到对象中。这是因为
urlopen
返回的web请求的结果不支持搜索:

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'))
再次请注意,您必须
在中打开
文件,而不是以文本的形式打开,否则会出现
zipfile.BadZipFile:文件不是zip文件
错误

将所有这些东西用作上下文管理器是一个很好的实践