使用Python提取ZipFile,显示进度百分比?

问题描述 投票:0回答:5

我知道如何使用 Python 提取 zip 存档,但是如何以百分比形式显示提取进度?

python zip extract progress
5个回答
20
投票

我建议使用

tqdm
,您可以使用
pip
安装它,如下所示:

pip install tqdm

然后就可以直接使用了:

>>> from tqdm import tqdm
>>>
>>> with zipfile.ZipFile(some_source) as zf:
...     for member in tqdm(zf.infolist(), desc='Extracting '):
...         try:
...             zf.extract(member, target_path)
...         except zipfile.error as e:
...             pass

这会产生类似这样的结果:

Extracting : 100%|██████████| 60.0k/60.0k [14:56<00:00, 66.9File/s]

10
投票

extract 方法不提供对此的回调,因此必须使用

getinfo
来获取 e 未压缩的大小,然后打开从块中读取的文件并将其写入您想要文件所在的位置并更新百分比,如果需要一个示例,还必须恢复 mtime:

import zipfile
z = zipfile.ZipFile(some_source)
entry_info = z.getinfo(entry_name)
i = z.open(entry_name)
o = open(target_name, 'w')
offset = 0
while True:
    b = i.read(block_size)
    offset += len(b)
    set_percentage(float(offset)/float(entry_info.file_size) * 100.)
    if b == '':
        break
    o.write(b)
i.close()
o.close()
set_attributes_from(entry_info)

这会将

entry_name
提取到
target_name


其中大部分也是由

shutil.copyfileobj
完成的,但也没有进度回调

ZipFile.extract
方法调用
_extract_member
的来源使用:

source = self.open(member, pwd=pwd)
target = file(targetpath, "wb")
shutil.copyfileobj(source, target)
source.close()
target.close()

如果成员不是 ZipInfo 对象,则已通过

getinfo(member)
将其从名称转换为 ZipInfo 对象


3
投票

抱歉有点晚才看到这个。有类似的问题,需要相当于

zipfile.Zipfile.extractall
。如果您有
tqdm>=4.40.0
(我一年多前发布的),那么:

from os import fspath
from pathlib import Path
from shutil import copyfileobj
from zipfile import ZipFile
from tqdm.auto import tqdm  # could use from tqdm.gui import tqdm
from tqdm.utils import CallbackIOWrapper

def extractall(fzip, dest, desc="Extracting"):
    """zipfile.Zipfile(fzip).extractall(dest) with progress"""
    dest = Path(dest).expanduser()
    with ZipFile(fzip) as zipf, tqdm(
        desc=desc, unit="B", unit_scale=True, unit_divisor=1024,
        total=sum(getattr(i, "file_size", 0) for i in zipf.infolist()),
    ) as pbar:
        for i in zipf.infolist():
            if not getattr(i, "file_size", 0):  # directory
                zipf.extract(i, fspath(dest))
            else:
                with zipf.open(i) as fi, open(fspath(dest / i.filename), "wb") as fo:
                    copyfileobj(CallbackIOWrapper(pbar.update, fi), fo)

1
投票

对于懒人来说,下面是一个基于 Dan D 的回答的独立工作示例。在 Python 3.10.6 上测试。未优化,但有效。

在此示例中,假设目标“test”目录存在,但您当然可以在提取函数中创建它。

与我在本主题中看到的大多数答案相比,Dan 的答案的优点是,如果存档由非常大的文件组成,则每次处理存档中的文件时显示进度并不能达到目标。

import zipfile
import os
from pathlib import Path

def extract(zip_path, target_path):
    block_size = 8192
    z = zipfile.ZipFile(zip_path)
    for entry_name in z.namelist():
        entry_info = z.getinfo(entry_name)
        i = z.open(entry_name)
        print(entry_name)
        if entry_name[-1] != '/':
            dir_name = os.path.dirname(entry_name)
            p = Path(f"{target_path}/{dir_name}")
            p.mkdir(parents=True, exist_ok=True)
            o = open(f"{target_path}/{entry_name}", 'wb')
            offset = 0
            while True:
                b = i.read(block_size)
                offset += len(b)
                print(float(offset)/float(entry_info.file_size) * 100.)
                if b == b'':
                    break
                o.write(b)
            o.close()
        i.close()
    z.close()

extract("test.zip", "test")

0
投票
import zipfile
srcZipFile = 'srcZipFile.zip'
distZipFile = 'distZipFile'
with zipfile.ZipFile(srcZipFile) as zf:
    filesList = zf.namelist()
    for idx, file in enumerate(filesList):
        percent = round((idx / len(filesList))*100)
        print(percent)
        zf.extract(file, distZipFile)
    zf.close()
© www.soinside.com 2019 - 2024. All rights reserved.