如何提取压缩文件中某个文件夹内的文件?

3 投票
1 回答
10257 浏览
提问于 2025-04-17 20:42

我需要从一个叫做 QuickLooks 的文件夹里提取一个名为 Preview.pdf 的文件,而这个文件夹是在一个压缩文件(zip 文件)里面。

现在我的代码大概是这样的:

with ZipFile(newName, 'r') as newName:
        newName.extract(\QuickLooks\Preview.pdf)
        newName.close()

在这个例子中,newName 被设置为压缩文件的完整路径。

需要注意的是,因为我在 Windows 系统上,所以这里的反斜杠是正确的。

不过这段代码并没有成功运行,出现了这样的错误:

Traceback (most recent call last):
  File "C:\Users\User\Documents\Folder\Python_Scripts\pageszip.py", line 18, in <module>
    ZF.extract("""QuickLooks\Preview.pdf""")
  File "C:\Python33\lib\zipfile.py", line 1019, in extract
    member = self.getinfo(member)
  File "C:\Python33\lib\zipfile.py", line 905, in getinfo
    'There is no item named %r in the archive' % name)
KeyError: "There is no item named 'QuickLook/Preview.pdf' in the archive"

我是在 Notepad++ 里面运行这个 Python 脚本,并从它的控制台获取输出。

我该怎么做才能实现这个功能呢?

另外,我也想知道如何提取整个 QuickLooks 文件夹,拿出 Preview.pdf,然后删除这个文件夹和里面的其他内容?

为了让你更了解情况,这里是脚本的其余部分。这个脚本是用来将 .pages 文件转换成 PDF 的。我知道有一些专门的转换工具,但我只是想通过这个练习来做一些实际应用。

import os.path
import zipfile
from zipfile import *
import sys

file = raw_input('Enter the full path to the .pages file in question. Please note that file and directory names cannot contain any spaces.')
dir = os.path.abspath(os.path.join(file, os.pardir))
fileName, fileExtension = os.path.splitext(file)
if fileExtension == ".pages":
    os.chdir(dir)
    print (dir)
    fileExtension = ".zip"
    os.rename (file, fileName + ".zip")
    newName = fileName + ".zip"  #for debugging purposes
    print (newName) #for debugging purposes
    with ZipFile(newName, 'w') as ZF:
        print("I'm about to list names!")
        print(ZF.namelist()) #for debugging purposes
        ZF.extract("QuickLook/Preview.pdf")
    os.rename('Preview.pdf', fileName + '.pdf')
    finalPDF = fileName + ".pdf"
    print ("Check out the PDF! It's located at" + dir +  finalPDF + ".")
else:
    print ("Sorry, this is not a valid .pages file.")
    sys.exit

我不确定引入 Zipfile 是否多余;我在另一个 StackOverflow 的帖子上看到说用 from zipfile import *import zipfile 更好。我不太确定,所以我两个都用了。=)

编辑:我已经根据 Blckknght 的建议修改了代码。

1 个回答

4

这里有一些看起来有效的解决方案。你的代码有几个问题。正如我在评论中提到的,打开zip文件时必须使用'r'模式,这样才能读取它。另一个问题是,zip压缩包中的文件名在路径中总是使用正斜杠/作为分隔符(可以参考PKZIP应用说明的4.4.17.1节)。需要注意的是,使用Python当前的zipfile模块,无法将嵌套的压缩包成员提取到不同的子目录。你可以控制根目录,但无法控制其下的内容(也就是说,zip文件内部的任何子文件夹)。

最后,由于不需要将.pages文件重命名为.zip——你传给ZipFile()的文件名可以有任何扩展名——我把这些内容从代码中去掉了。不过,为了克服将成员提取到不同子目录的限制,我不得不先将目标成员提取到一个临时目录,然后再复制到最终目的地。当然,提取完后,这个临时文件夹需要被删除。所以我不确定最终的结果是否真的更简单……

import os.path
import shutil
import sys
import tempfile
from zipfile import ZipFile

PREVIEW_PATH = 'QuickLooks/Preview.pdf'  # archive member path
pages_file = input('Enter the path to the .pages file in question: ')
#pages_file = r'C:\Stack Overflow\extract_test.pages'  # hardcode for testing
pages_file = os.path.abspath(pages_file)
filename, file_extension = os.path.splitext(pages_file)
if file_extension == ".pages":
    tempdir = tempfile.gettempdir()
    temp_filename = os.path.join(tempdir, PREVIEW_PATH)
    with ZipFile(pages_file, 'r') as zipfile:
        zipfile.extract(PREVIEW_PATH, tempdir)
    if not os.path.isfile(temp_filename):  # extract failure?
        sys.exit('unable to extract {} from {}'.format(PREVIEW_PATH, pages_file))
    final_PDF = filename + '.pdf'
    shutil.copy2(temp_filename, final_PDF)  # copy and rename extracted file
    # delete the temporary subdirectory created (along with pdf file in it)
    shutil.rmtree(os.path.join(tempdir, os.path.split(PREVIEW_PATH)[0]))
    print('Check out the PDF! It\'s located at "{}".'.format(final_PDF))
    #view_file(final_PDF)  # see Bonus below
else:
    sys.exit('Sorry, that isn\'t a .pages file.')

附加内容:如果你想在脚本中实际查看最终的pdf文件,可以添加以下函数,并在创建的最终pdf上使用它(假设你的系统上安装了PDF查看应用程序):

import subprocess
def view_file(filepath):
    subprocess.Popen(filepath, shell=True).wait()

撰写回答