用Python ZipFile提取zip文件中的文件而不保留结构?

75 投票
5 回答
76362 浏览
提问于 2025-04-16 11:21

我想从一个包含子文件夹的.zip文件中提取所有文件,并把它们放到一个文件夹里。我希望把子文件夹里的所有文件都提取到同一个文件夹中,而不保留原来的文件夹结构。目前,我是先提取所有文件,然后把文件移动到一个文件夹里,再删除之前的子文件夹。这样的话,如果有同名的文件,会被覆盖。

我想知道有没有办法在写入文件之前就做到这一点?

这里有一个示例结构:

my_zip/file1.txt
my_zip/dir1/file2.txt
my_zip/dir1/dir2/file3.txt
my_zip/dir3/file4.txt

最后我希望得到这样的结果:

my_dir/file1.txt
my_dir/file2.txt
my_dir/file3.txt
my_dir/file4.txt

我可以在这段代码中添加什么呢?

import zipfile
my_dir = "D:\\Download\\"
my_zip = "D:\\Download\\my_file.zip"

zip_file = zipfile.ZipFile(my_zip, 'r')
for files in zip_file.namelist():
    zip_file.extract(files, my_dir)
zip_file.close()

如果我在zip_file.namelist()中重命名文件路径,我会遇到这个错误:

KeyError: "There is no item named 'file2.txt' in the archive"

5 个回答

16

你可以直接把文件提取到内存中,计算出文件名,然后自己把它写进去,而不是让库来处理这个事情。大部分情况下,只需要用“read()”方法,而不是“extract()”方法就可以了。

Python 3.6+ 更新(2020) - 这里的代码和原来的答案一样,但使用了 pathlib.Path,这样可以更方便地处理文件路径和其他操作(比如“write_bytes”)。

from pathlib import Path
import zipfile
import os

my_dir = Path("D:\\Download\\")
my_zip = my_dir / "my_file.zip"

zip_file = zipfile.ZipFile(my_zip, 'r')
for files in zip_file.namelist():
    data = zip_file.read(files, my_dir)
    myfile_path = my_dir / Path(files.filename).name
    myfile_path.write_bytes(data)
zip_file.close()

原始代码(没有使用 pathlib)

import zipfile
import os

my_dir = "D:\\Download\\"
my_zip = "D:\\Download\\my_file.zip"

zip_file = zipfile.ZipFile(my_zip, 'r')
for files in zip_file.namelist():
    data = zip_file.read(files, my_dir)
    # I am almost shure zip represents directory separator
    # char as "/" regardless of OS, but I  don't have DOS or Windos here to test it
    myfile_path = os.path.join(my_dir, files.split("/")[-1])
    myfile = open(myfile_path, "wb")
    myfile.write(data)
    myfile.close()
zip_file.close()
59

你可以通过 ZipFile.infolist() 来遍历一个压缩文件里的内容。对于返回的 ZipInfo 对象,你可以处理它的 filename,把路径部分去掉,最后把文件提取到你指定的文件夹里。

import zipfile
import os

my_dir = "D:\\Download\\"
my_zip = "D:\\Download\\my_file.zip"

with zipfile.ZipFile(my_zip) as zip:
    for zip_info in zip.infolist():
        if zip_info.is_dir():
            continue
        zip_info.filename = os.path.basename(zip_info.filename)
        zip.extract(zip_info, my_dir)
85

这段话的意思是,它打开了压缩文件里每个文件的连接,提取出文件名,然后把这个文件名复制到一个目标文件中(这就是ZipFile.extract的工作方式,不过它不处理子目录)。

import os
import shutil
import zipfile

my_dir = r"D:\Download"
my_zip = r"D:\Download\my_file.zip"

with zipfile.ZipFile(my_zip) as zip_file:
    for member in zip_file.namelist():
        filename = os.path.basename(member)
        # skip directories
        if not filename:
            continue
    
        # copy file (taken from zipfile's extract)
        source = zip_file.open(member)
        target = open(os.path.join(my_dir, filename), "wb")
        with source, target:
            shutil.copyfileobj(source, target)

撰写回答