Python脚本:

在n个压缩包中,提取pdf文件。本文中代码有Qoder CN生成。

在整理毕业设计的材料时,需要让学生提交一个最终材料的压缩包,另外提交一个论文和指导手册的pdf文件。但是很多同学忘记交pdf了,那就用下面的程序提取。

1. 下面是5个同学的压缩包,压缩包中包含pdf文件,我需要把他们的pdf文件提取出来。

2. 使用程序:需要修改程序中的第224行SOURCE_FOLDER改为自己的文件夹路径。

import os
import zipfile
import tarfile
import shutil
import subprocess
from pathlib import Path
"""_summary_
把压缩包中的 PDF 文件解压并保存为单独的文件,在最终材料中提取论文与指导手册得pdf文件

Returns:
    _type_: _description_
"""

# 尝试导入可选的库以支持更多格式
try:
    import py7zr
    HAS_7Z_LIB = True
except ImportError:
    HAS_7Z_LIB = False

try:
    import rarfile
    HAS_RAR_LIB = True
except ImportError:
    HAS_RAR_LIB = False

# 配置 7-Zip 路径 (根据你的安装位置修改)
# 优先尝试系统 PATH 中的 7z,如果没有,尝试常见安装路径
SEVEN_ZIP_PATH = '7z'


def is_tool_available(cmd):
    """检查命令行工具是否可用"""
    try:
        subprocess.run([cmd, '--help'],
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE,
                       timeout=5)
        return True
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return False


if not is_tool_available('7z'):
    possible_paths = [r"D:\InstallProgram\7-Zip\7z.exe"]
    for p in possible_paths:
        if os.path.exists(p):
            SEVEN_ZIP_PATH = p
            break


def get_unique_filename(filepath):
    """如果文件已存在,生成一个新的唯一文件名 (例如 file(1).pdf)"""
    if not os.path.exists(filepath):
        return filepath

    base, ext = os.path.splitext(filepath)
    counter = 1
    while os.path.exists(filepath):
        filepath = f"{base}({counter}){ext}"
        counter += 1
    return filepath


def flatten_output_directory(output_dir):
    """
    将 output_dir 下所有子文件夹中的文件移动到 output_dir 根目录
    并删除空的子文件夹
    """
    if not os.path.exists(output_dir):
        return

    # 遍历目录树
    for root, dirs, files in os.walk(output_dir, topdown=False):
        # 跳过根目录本身
        if root == output_dir:
            continue

        # 移动文件到根目录
        for filename in files:
            src_path = os.path.join(root, filename)
            dst_path = os.path.join(output_dir, filename)

            # 处理文件名冲突
            dst_path = get_unique_filename(dst_path)

            try:
                shutil.move(src_path, dst_path)
                # print(f"  移动文件: {filename} 到根目录")
            except Exception as e:
                print(f"  移动文件失败 {filename}: {e}")

        # 删除空文件夹 (os.walk topdown=False 保证先处理子目录)
        try:
            if not os.listdir(root):  # 如果文件夹为空
                os.rmdir(root)
        except Exception as e:
            pass  # 忽略删除错误


def extract_pdf_from_zip(zip_path, output_dir):
    """从 zip 文件中提取 PDF (支持 GBK 编码文件名)"""
    try:
        with zipfile.ZipFile(zip_path, 'r', metadata_encoding='gbk') as z:
            for file_info in z.infolist():
                if file_info.is_dir():
                    continue
                if file_info.filename.lower().endswith('.pdf'):
                    filename = os.path.basename(file_info.filename)
                    if not filename or not filename.strip():
                        continue
                    target_path = os.path.join(output_dir, filename)
                    target_path = get_unique_filename(target_path)
                    try:
                        with z.open(file_info) as source, open(
                                target_path, 'wb') as target:
                            shutil.copyfileobj(source, target)
                    except Exception as e:
                        print(f"  提取 ZIP 中的 PDF 失败 {filename}: {e}")
    except Exception as e:
        print(f"错误: 无法打开或解压 ZIP 文件 {zip_path}: {e}")


def extract_pdf_from_tar(tar_path, output_dir):
    """从 tar/tar.gz 文件中提取 PDF"""
    mode = 'r:gz' if tar_path.endswith('.gz') else 'r'
    try:
        with tarfile.open(tar_path, mode) as t:
            for member in t.getmembers():
                if member.isfile() and member.name.lower().endswith('.pdf'):
                    filename = os.path.basename(member.name)
                    if filename:
                        target_path = os.path.join(output_dir, filename)
                        target_path = get_unique_filename(target_path)
                        with t.extractfile(member) as source, open(
                                target_path, 'wb') as target:
                            shutil.copyfileobj(source, target)
    except Exception as e:
        print(f"错误: 无法解压 {tar_path}: {e}")


def extract_pdf_from_7z(sevenz_path, output_dir):
    """从 7z 文件中仅提取 PDF"""
    _extract_archive_with_filter(sevenz_path, output_dir, '7z')


def extract_pdf_from_rar(rar_path, output_dir):
    """从 rar 文件中仅提取 PDF"""
    _extract_archive_with_filter(rar_path, output_dir, 'rar')


def _extract_archive_with_filter(archive_path, output_dir, archive_type):
    """
    通用函数:使用 7-Zip 命令行工具,先列表筛选,再仅提取 PDF
    archive_type: '7z' or 'rar'
    """
    if not os.path.exists(archive_path):
        print(f"错误: 文件不存在 {archive_path}")
        return

    # 1. 列出压缩包内的所有文件
    # 7z l "archive.rar" -ba -slt 列出详细技术信息,便于解析
    cmd_list = [SEVEN_ZIP_PATH, 'l', archive_path, '-ba', '-slt']

    pdf_files_in_archive = []

    try:
        result = subprocess.run(cmd_list,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        if result.returncode != 0:
            print(
                f"  无法列出 {archive_type} 内容: {result.stderr.decode('gbk', errors='ignore')}"
            )
            return

        # 解析输出,寻找 Path 行
        output_text = result.stdout.decode('gbk',
                                           errors='ignore')  # 7z 中文输出通常是 gbk
        lines = output_text.splitlines()

        current_path = None
        for line in lines:
            if line.startswith('Path = '):
                current_path = line[7:].strip()
                if current_path.lower().endswith('.pdf'):
                    pdf_files_in_archive.append(current_path)

    except Exception as e:
        print(f"  解析 {archive_type} 列表时出错: {e}")
        return

    if not pdf_files_in_archive:
        print(f"  未在 {os.path.basename(archive_path)} 中找到 PDF 文件")
        return

    # 2. 仅提取筛选出的 PDF 文件
    # 7z x "archive.rar" -o"output_dir" -y "file1.pdf" "file2.pdf"
    if pdf_files_in_archive:
        # 构建提取命令
        cmd_extract = [
            SEVEN_ZIP_PATH, 'x', archive_path, f'-o{output_dir}', '-y'
        ] + pdf_files_in_archive

        try:
            result = subprocess.run(cmd_extract,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE)
            if result.returncode == 0:
                print(
                    f"  成功从 {archive_type} 中提取了 {len(pdf_files_in_archive)} 个 PDF"
                )
                # 3. 扁平化处理:将子文件夹中的 PDF 移到根目录
                flatten_output_directory(output_dir)
            else:
                err_msg = result.stderr.decode('gbk', errors='ignore')
                print(f"  提取 {archive_type} 中的 PDF 失败: {err_msg}")
        except Exception as e:
            print(f"  执行提取命令时出错: {e}")


def main():
    # 配置部分
    SOURCE_FOLDER = 'C:/Users/Downloads/软件工程2309-2310-第四章作业/(pdf)'
    OUTPUT_FOLDER = 'extracted_pdfs'

    os.makedirs(OUTPUT_FOLDER, exist_ok=True)

    supported_extensions = ('.zip', '.tar', '.gz', '.tgz', '.7z', '.rar')

    processed_count = 0

    for filename in os.listdir(SOURCE_FOLDER):
        file_path = os.path.join(SOURCE_FOLDER, filename)

        if not os.path.isfile(file_path):
            continue

        lower_name = filename.lower()
        if not any(lower_name.endswith(ext) for ext in supported_extensions):
            continue

        print(f"正在处理: {filename}")

        try:
            if lower_name.endswith('.zip'):
                extract_pdf_from_zip(file_path, OUTPUT_FOLDER)
            elif lower_name.endswith(('.tar', '.gz', '.tgz')):
                extract_pdf_from_tar(file_path, OUTPUT_FOLDER)
            elif lower_name.endswith('.7z'):
                extract_pdf_from_7z(file_path, OUTPUT_FOLDER)
            elif lower_name.endswith('.rar'):
                extract_pdf_from_rar(file_path, OUTPUT_FOLDER)

            processed_count += 1
        except Exception as e:
            print(f"处理文件 {filename} 时发生未知错误: {e}")

    print(f"\n完成! 共处理了 {processed_count} 个压缩包。")
    print(f"PDF 文件已保存到: {os.path.abspath(OUTPUT_FOLDER)}")


if __name__ == '__main__':
    main()

3.运行程序,显示处理了5个压缩包,提取的文件放到了extracted_pdfs文件夹中。

提取pdf文件完成


本文中代码有Qoder CN生成。

阿里云推出的AI智能体产品系列

Qoder CN,阿里云推出的AI智能体产品系列,覆盖软件开发与日常办公多元场景,包含面向编码场景的Qoder CN、面向日常工作的QoderWork CN、Qoder CN CLI等子产品。Qoder CN前身为阿里云旗下智能编码助手通义灵码。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐