是否可以从zipfile加载带有openpyxl的xlsx

时间:2017-06-08 21:17:48

标签: excel python-2.7 openpyxl

我正在尝试从压缩的zip文件中打开openxxl.load_workbook xlsx文件,但它不起作用。以下代码在openpyxl.load_workbook中失败并显示“BadZipfile:File is not a zip file”

with zipfile.ZipFile(os.path.join(root, raw)) as z:
    for file_info in z.infolist():
        wb = openpyxl.load_workbook(z.open(file_info), read_only=True)

归档文件及其中的excel文件没有任何问题,就像我将其提取到磁盘然后以下工作:

with open('report.xlsx') as f:
    wb = openpyxl.load_workbook(f, read_only=True)

我可以使用此解决方案并临时解压缩并加载xslx,但是想了解是否可以从zipfile加载它。

1 个答案:

答案 0 :(得分:4)

问题在于readonly=True没有做到你认为的那样。根据{{​​3}}:

  

幸运的是,有两种模式可以让您在(接近)恒定内存消耗的情况下读取和写入无限量的数据。

虽然没有明确说明,但我认为这涉及一些等同于内存映射文件(因为"常量内存消耗")和随机访问(因为允许操作的范围)。 / p>

无论哪种方式,设置readonly=True并不表示您只打算阅读工作簿(无论如何load_workbook都可以执行,您必须覆盖现有工作簿进行任何改变")。这表明您希望直接在磁盘上访问该文件,而无需加载整个内容。

似乎很清楚(并且直观地预期)docs不提供随机访问文件:

  

注意:类似文件的对象是只读的,并提供以下方法:read()readline()readlines()__iter__()next()

此列表中未提及seek的事实非常明显(双关语只是有点意图)。

您可以通过将有问题的行拆分为两个来获得有关异常的更多信息(对于嵌套函数调用,这是一种有用的常规调试技术):

x = z.open(file_info)
wb = openpyxl.load_workbook(x, readonly=True)

您会注意到这两行中的第二行发生错误。这是因为几乎所有的Microsoft开放文档格式实际上只是花哨的zip文件。问题很可能是openpyxl无法以随机访问模式打开您的文件,而不是它实际上是无效的zip文件。

无论哪种方式,这是一堆非常有根据的猜测,导致一个简单的,一个关键字删除解决方案:

TL; DR

在读取非压缩zip条目等非随机访问数据时删除readonly=True

wb = openpyxl.load_workbook(z.open(file_info))

附录

你应该养成写出能够证明你的问题的最小程序的习惯,以便回答你问题的人可以集中精力完成自己的工作,而不是烦恼并关闭本来是一个非常好的问题。我喜欢你的问题足以为你做到这一点,所以这里有一个最小的程序来演示你的问题,只需复制并粘贴即可运行:

import openpyxl, zipfile
from openpyxl.workbook.workbook import Workbook

wb = Workbook()
wb.active['A1'] = 12
wb.active['A2'] = 13
wb.save('report.xlsx')

with zipfile.ZipFile('test.zip', 'w') as z:
    z.write('report.xlsx')

with open('report.xlsx') as f:
    wb = openpyxl.load_workbook(f, read_only=True)
    print(wb.active['A1'].value)
    print(wb.active['A2'].value)

with zipfile.ZipFile('test.zip', 'r') as z:
    for file_info in z.infolist():
        x = z.open(file_info, 'r')
        wb = openpyxl.load_workbook(x, readonly=True)
        print(wb.active['A1'].value)
        print(wb.active['A2'].value)