是否可以从 zipfile 使用 openpyxl 加载 xlsx

Is it possible to load xlsx with openpyxl from zipfile

我正在尝试 openpyxl.load_workbook 从压缩的 zip 文件中提取 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 加载它。

问题是 readonly=True 并不像您想象的那样。根据 docs:

Fortunately, there are two modes that enable you to read and write unlimited amounts of data with (near) constant memory consumption.

虽然没有明确说明,但我认为这涉及到内存映射文件(因为“持续内存消耗”)和随机访问(因为允许的操作范围)等价物。

无论哪种方式,设置 readonly=True 并不表示您只打算阅读一本工作簿(这就是 load_workbook 无论如何都可以做的,您必须覆盖现有的才能使任何变化”)。这表明您想要直接访问磁盘上的文件,而不加载全部内容。

ZipFile.open 不提供随机访问文件似乎很清楚(并且直觉上可以预期):

Note: The file-like object is read-only and provides the following methods: 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)