基于 Python 批量提取 PDF 首页

最近因为汇总论文成果,需要提取论文的首页。虽然 Acrobat 提供了页面组织功能可以任意提取,但手动操作一次只能处理一个文件。当面对好几十篇论文时,尚未开始就已经放弃。

工欲善其事必先利其器,决定造个工具,一劳永逸提高效率。

以下代码基于 小白程序猿 的方案,面向我自己的需求作了一些调整适配,在此致谢🫡

插播推荐 PDF 补丁丁
一个满满情怀的 PDF 神器:PDF 补丁丁,已开源在PDFPatcher,三方长文教程:免费了 12 年的神器开源了,你可能不知道它有多强大
  • Python

  • PyMuPDF模块

1
pip install PyMuPDF

以下为完整代码:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# coding:utf-8
import os
import fitz

# 解析
# 函数将file_path中所有文件(包括子目录中的文件),处理后,按照原目录保存到save_path下。
# 即save_path下的文件夹结构和file_path一致。
def analysis(file_path, save_path, num, toimg):
    # 资源列表
    file_array = []
    if os.path.isdir(file_path):
        # 目录循环压入
        file_count = get_path_file(file_path)
        for v in file_count:
            file_array.append(v)
    else:
        # 单文件,单次调用
        file_array.append(file_path)

    # 判断为空情况
    if not file_array:
        print("此目录下无文件")
    # 执行解析
    file_count_num = len(file_array)
    print("程序运行中,共计%s个文件" % file_count_num)
    success_num = file_count_num
    failed_file_array = []
    for v in file_array:
        print("原文件路径:%s" % v)
        # 获取文件名称及类型
        file_name = os.path.basename(v)
        # print("文件信息:%s" % file_name)
        if '.pdf' not in file_name:
            print("此文件非PDF文件,跳过")
            failed_file_array.append("非 PDF文件:" + file_name)
            success_num = success_num - 1
            continue
        #  打开PDF文件,生成一个对象
        doc = fitz.open(v)
        # 总页数
        count_page = doc.pageCount
        # print("文件共计:%s页" % count_page)
        if toimg == False:
            # 仅提取页面
            if count_page > 1:
                doc2 = fitz.open()      # 创建新的空PDF
                doc2.insert_pdf(doc, to_page = num)  # 提取doc的第1页到doc2
                # 创建子目录
                p_1 = v.replace(file_path, save_path)
                p_2 = p_1.replace(file_name, '')
                if not os.path.exists(p_2):
                    os.makedirs(p_2)
                print("提取到路径:" + p_2 + file_name)
                doc2.save(p_2 + file_name)    # 保存提取出的PDF文件
                print("提取完成")
            else:
                print("此文档无内容,跳过")
                failed_file_array.append("文件无内容:" + file_name)
                success_num = success_num - 1
                continue
        else:
            # 将页面转换为图片
            if count_page > 1:
                page = doc[num]
                rotate = int(0)
                # 每个尺寸的缩放系数为2,这将为我们生成分辨率提高四倍的图像。
                zoom_x = 2.0
                zoom_y = 2.0
                trans = fitz.Matrix(zoom_x, zoom_y).preRotate(rotate)
                pm = page.getPixmap(matrix=trans, alpha=False)
                # 保存路径
                # 创建子目录
                p_1 = v.replace(file_path, save_path)
                p_2 = p_1.replace(file_name, '')
                if not os.path.exists(p_2):
                    os.makedirs(p_2)
                
                new_file_name = file_name.replace(".pdf", "")
                print("提取到路径:" + p_2 + '%s.png' % new_file_name)
                pm.writePNG(p_2 + '%s.png' % new_file_name)
                print("提取并转换为图片完成")
            else:
                print("此文档无内容,跳过")
                failed_file_array.append(file_name)
                success_num = success_num - 1
                continue
    print("\n\n合计 %d 个文件提取成功,以下文件提取失败:" % success_num)
    for f in failed_file_array:
        print(f)

# 返回目录下所有文件
def get_path_file(files_path):
    data = []
    for root, dirs, files in os.walk(files_path, topdown=False):
        for name in files:
            f_p = os.path.join(root, name).replace("\\", "/")
            data.append(f_p)
    return data

# 规范路径的斜杠格式
def uni_path(path: str) -> str:
    # return path.replace(r'\/'.replace(os.sep, ''), os.sep)  # 转换为当前系统的格式
    return path.replace("\\\\","/").replace("\\","/") # 统一转换为unix格式

if __name__ == '__main__':
    print("|---------------------------------|")
    print("|++++++++  PDF处理工具箱  ++++++++|")
    print("|---------------------------------|")
    print("|                                 |")
    print("| 1. PDF 批量提取首页             |")
    print("| 2. PDF 批量提取首页并转换为图片 |")
    print("|                                 |")
    print("|---------------------------------|")

    # 选择处理方式
    toimg = False
    choice = input("请输入要执行操作的编号:")
    if choice == '1':
        toimg = False
    elif choice == '2':
        toimg =True
    # 当前目录下的文件
    now_path = os.getcwd()
    print("当前位置:%s" % now_path)
    # 保存路径
    print("请输入参数,以 / 结尾")
    save_path = input("提取文件保存地址:")
    # 判断目录
    save_path_status = os.path.exists(save_path)
    if not save_path_status:
        os.mkdir(save_path)
    # 截取页数
    num = 0 # 截取第一页
    # 路径或文件名
    file_path = input("待处理PDF文件地址:")

    # 调用方法
    analysis(uni_path(file_path), uni_path(save_path), num, toimg)

Python 提取 PDF 第一页为封面图片【批量提取】

Python 把任意系统的路径转换成当前系统的格式(关于 / \ 分隔符的)

python中os.walk的用法

python PyMuPDF(fitz)包中insert_pdf的用法