Python自动化PDF合并:PyMuPDF实战指南与脚本开发
1. 从“手动拖拽”到“一键脚本”为什么我们需要自动化PDF合并如果你经常和PDF文件打交道尤其是处理报告、论文、合同或者从不同系统导出的零散文档那么“合并PDF”这个操作对你来说一定不陌生。最原始的做法是什么打开某个PDF编辑器点击“合并文件”然后一个个选择、拖拽、调整顺序最后生成一个新文件。偶尔做一次还行但当你每周、甚至每天都要重复这个枯燥的流程或者需要处理的文件数量动辄几十上百个时这种手动操作就成了一种折磨。效率低下不说还极易出错比如漏掉某个文件或者顺序排错。这正是我当初决定用Python来解决这个问题的原因。市面上虽然有大量优秀的图形化PDF工具但它们往往存在几个痛点一是批量处理不够灵活尤其是需要按复杂规则如按文件名、时间戳排序合并时二是很多高级功能需要付费三是无法集成到自动化工作流中。比如我经常需要将每日生成的数十份数据报告自动合并成一份周报手动操作根本不可行。Python的PyPDF2、PyMuPDFfitz、pdfrw等库为我们提供了强大的程序化处理PDF的能力。通过编写一个简单的脚本你可以实现按指定文件夹内文件创建时间自动排序合并、仅合并特定页码、在合并时添加页眉页脚或水印、甚至基于文件内容智能分类后再合并。这不仅仅是节省几分钟时间而是将一项繁琐、易错的人工劳动转变为一个可靠、可重复、可定制的自动化流程。接下来我将从一个实际需求场景出发带你一步步构建一个比“傻瓜式”合并工具更强大、更贴合你个人工作流的Python PDF合并工具。2. 核心工具选型PyPDF2、PyMuPDF 与 pdfrw 的实战对比选择正确的库是项目成功的基石。Python生态中处理PDF的库不少但专注于合并拼接功能的主要有三个主流选择PyPDF2以及其维护分支PyPDF4、pypdf、PyMuPDFfitz和pdfrw。它们各有优劣适用的场景也不同。我通过大量实际项目总结出了下面的对比表格你可以根据自己的需求快速决策。特性/库名PyPDF2 / pypdfPyMuPDF (fitz)pdfrw主要优势纯Python实现API简单直观专注于PDF的“读写合并”等基础操作学习曲线平缓。基于强大的MuPDF引擎速度极快功能全面渲染、解析、编辑对复杂PDF兼容性好。设计优雅能较好地保持PDF的原始结构如表单、注释在“读取-修改-写入”场景下表现优秀。合并性能中等。处理简单文档足够但面对上百页或内含大量资源的PDF时速度较慢内存消耗可能较高。卓越。无论是合并速度还是内存效率都是三者中最高的特别适合处理大批量、大体积的PDF。中等。性能介于PyPDF2和PyMuPDF之间对于常规合并任务足够。功能丰富度基础。支持合并、拆分、旋转、加水印、加元数据。对加密PDF支持有限处理某些复杂PDF可能出错。非常丰富。除基础操作外还支持精确的文本/图像提取、高级搜索、PDF渲染为图片、注释处理等。专注于内容保留。能很好地读取和写入书签目录、表单、注释等适合需要保持这些元素的合并场景。安装复杂度非常简单pip install pypdf即可。稍复杂。需要系统预装MuPDF的C库通常pip install PyMuPDF会尝试自动编译在某些Windows环境下可能需要额外步骤。简单pip install pdfrw。推荐使用场景快速原型验证处理简单、少量的PDF合并任务对依赖纯净性要求高纯Python。生产环境首选。需要高性能、处理复杂或大量PDF、或未来可能扩展更多高级功能如OCR后处理的项目。合并时需要完美保留原始PDF中的交互式元素如表单字段、书签的项目。注意PyPDF2的原仓库已不再积极维护社区出现了多个分支。目前最推荐使用的是pypdfpip install pypdf它是PyPDF2的一个积极维护的forkAPI兼容且修复了大量bug。下文如无特别说明提到的PyPDF2均指代pypdf库。基于以上对比我的个人建议是对于绝大多数以“合并”为核心需求的自动化脚本优先选择PyMuPDF。它的性能优势在批量操作时是决定性的而且其强大的底层能力也为脚本未来的功能扩展比如合并前先检查页面内容留足了空间。当然如果你的需求仅仅是快速写一个一次性脚本且PDF非常简单那么pypdf的简洁性也是不错的选择。pdfrw则在处理带有表单的合同、调查问卷等PDF合并时不可替代。3. 环境准备与基础合并脚本搭建在确定了使用PyMuPDF之后我们开始动手搭建环境。虽然它的能力强大但基础合并功能的代码其实非常简洁。3.1 创建虚拟环境与安装依赖首先为项目创建一个独立的虚拟环境是个好习惯这能避免不同项目间的库版本冲突。# 在项目目录下 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # Linux/Mac: source venv/bin/activate # 安装 PyMuPDF pip install PyMuPDF安装完成后可以通过pip list | findstr fitzWindows或pip list | grep fitzLinux/Mac来确认安装成功。3.2 编写第一个基础合并脚本我们来创建一个最简单的脚本merge_basic.py实现将指定列表中的PDF文件按顺序合并。import fitz # PyMuPDF的导入名是fitz import os import sys def merge_pdfs(pdf_list, output_filename): 基础合并函数 :param pdf_list: 待合并的PDF文件路径列表 :param output_filename: 输出文件路径 # 创建一个新的PDF文档对象 result_pdf fitz.open() for pdf_path in pdf_list: # 打开每一个源PDF src_pdf fitz.open(pdf_path) # 将源PDF的所有页面插入到结果文档的末尾 result_pdf.insert_pdf(src_pdf) # 关闭源文档释放资源 src_pdf.close() # 保存合并后的结果 result_pdf.save(output_filename) # 关闭结果文档 result_pdf.close() print(f合并完成文件已保存至{output_filename}) if __name__ __main__: # 示例合并当前目录下的1.pdf, 2.pdf, 3.pdf files_to_merge [1.pdf, 2.pdf, 3.pdf] # 在实际使用中你可以通过命令行参数、读取配置文件等方式来获取这个列表 # 这里先检查文件是否存在 valid_files [] for f in files_to_merge: if os.path.exists(f): valid_files.append(f) else: print(f警告文件 {f} 不存在已跳过。) if valid_files: merge_pdfs(valid_files, merged_output.pdf) else: print(错误没有找到任何有效的PDF文件进行合并。)这个脚本的核心是fitz.open()和insert_pdf()方法。fitz.open()用于打开一个PDF文件返回一个文档对象。insert_pdf()则是将另一个文档的所有页面插入到当前文档的指定位置。默认是追加到末尾所以循环调用就实现了顺序拼接。实操心得务必养成close()的习惯。虽然Python有垃圾回收但显式关闭文件对象可以立即释放内存和底层资源在处理大量或大型PDF时尤为重要能有效避免内存泄漏导致的程序崩溃。4. 进阶功能实现让合并脚本更智能、更强大基础合并只是开始。一个真正好用的工具必须能处理现实中的复杂情况。下面我们为脚本添加几个必备的进阶功能。4.1 自动扫描文件夹并按规则排序手动列出文件名太麻烦。更常见的场景是我有一个文件夹里面全是需要合并的PDF希望脚本能自动读取并按我想要的顺序比如文件名、修改时间合并。import fitz import os import glob from pathlib import Path def merge_pdfs_from_folder(folder_path, output_filename, sort_byfilename): 合并指定文件夹下的所有PDF文件 :param folder_path: 目标文件夹路径 :param output_filename: 输出文件路径 :param sort_by: 排序规则filename按文件名, mtime按修改时间, ctime按创建时间 # 使用Path对象处理路径更安全、方便 folder Path(folder_path) if not folder.is_dir(): print(f错误路径 {folder_path} 不是一个有效的文件夹。) return # 使用glob获取所有.pdf文件 pdf_files list(folder.glob(*.pdf)) if not pdf_files: print(f在文件夹 {folder_path} 中未找到任何PDF文件。) return # 根据指定规则排序 if sort_by filename: pdf_files.sort(keylambda x: x.name.lower()) # 忽略大小写排序 elif sort_by mtime: pdf_files.sort(keylambda x: x.stat().st_mtime) elif sort_by ctime: pdf_files.sort(keylambda x: x.stat().st_ctime) else: print(f警告未知的排序规则 {sort_by}将使用默认文件名排序。) pdf_files.sort(keylambda x: x.name.lower()) print(f找到 {len(pdf_files)} 个PDF文件将按以下顺序合并) for i, f in enumerate(pdf_files, 1): print(f {i}. {f.name}) # 调用基础合并函数 merge_pdfs([str(f) for f in pdf_files], output_filename) # 需要将之前的基础合并函数也定义在这里 def merge_pdfs(pdf_list, output_filename): # ... 同上一个代码块中的 merge_pdfs 函数 ... pass if __name__ __main__: # 示例合并当前目录下的所有PDF按修改时间排序 merge_pdfs_from_folder(., merged_by_mtime.pdf, sort_bymtime)这个改进使得脚本的实用性大大增强。你可以轻松地处理一个装满扫描件或导出报告的文件夹。4.2 选择性合并范围与密码处理有时我们并不需要合并整个文件或者文件被加密了。合并特定页面范围PyMuPDF的insert_pdf方法提供了from_page和to_page参数来指定页面范围页码从0开始。def merge_pdfs_selective(pdf_list, output_filename, page_rangesNone): 合并PDF并可选择每个PDF的页面范围 :param pdf_list: 待合并的PDF文件路径列表 :param output_filename: 输出文件路径 :param page_ranges: 可选。一个列表长度与pdf_list相同。 每个元素可以是一个元组 (start, end) 或 None。 None表示合并整个文件(start, end)表示合并[start, end)页左闭右开。 例如[(0, 3), None, (5, 10)] 表示合并第1个PDF的0-2页第2个PDF的全部第3个PDF的5-9页。 result_pdf fitz.open() if page_ranges is None: page_ranges [None] * len(pdf_list) elif len(page_ranges) ! len(pdf_list): print(错误page_ranges的长度必须与pdf_list相同。) return for pdf_path, page_range in zip(pdf_list, page_ranges): src_pdf fitz.open(pdf_path) if page_range is None: # 合并全部 result_pdf.insert_pdf(src_pdf) else: start, end page_range # 确保页码范围有效 if start 0 or end len(src_pdf) or start end: print(f警告文件 {pdf_path} 的页码范围 {page_range} 无效已跳过该文件。) src_pdf.close() continue # 合并指定范围 result_pdf.insert_pdf(src_pdf, from_pagestart, to_pageend-1) # to_page是包含的所以end-1 src_pdf.close() result_pdf.save(output_filename) result_pdf.close() print(f选择性合并完成文件已保存至{output_filename})处理加密PDF如果PDF有密码需要在打开时提供。def merge_encrypted_pdfs(pdf_list, output_filename, passwordsNone): 合并可能加密的PDF :param pdf_list: 文件路径列表 :param output_filename: 输出路径 :param passwords: 可选。密码列表与pdf_list一一对应。如果某个文件无密码对应位置为None。 result_pdf fitz.open() if passwords is None: passwords [None] * len(pdf_list) for pdf_path, pwd in zip(pdf_list, passwords): try: # 尝试用密码打开如果密码为None则正常打开 src_pdf fitz.open(pdf_path, passwordpwd) result_pdf.insert_pdf(src_pdf) src_pdf.close() print(f成功处理{pdf_path}) except Exception as e: print(f处理文件 {pdf_path} 时出错{e}。已跳过此文件。) # 可以选择继续处理下一个文件或者直接退出 continue if len(result_pdf) 0: # 如果至少合并了一页 result_pdf.save(output_filename) print(f合并完成已跳过出错文件。输出{output_filename}) else: print(错误未能成功合并任何页面。) result_pdf.close()4.3 添加统一页眉页脚与水印在合并商务报告或正式文档时为所有页面添加统一的页眉、页脚或水印能显得更专业。PyMuPDF允许我们通过操作页面的Page对象来实现。def add_watermark_to_page(page, textConfidential, font_size40): 在单个页面中心添加文本水印 :param page: fitz.Page对象 :param text: 水印文字 :param font_size: 字体大小 # 获取页面矩形页面大小 rect page.rect # 计算水印文本的大致宽度这是一个估算实际需根据字体精确计算 # 这里我们简单地将文本放在页面中心 text_width fitz.get_text_length(text, fontnamehelv, fontsizefont_size) center_x (rect.width - text_width) / 2 center_y rect.height / 2 # 在水印位置插入一个文本块 # 使用红色、半透明、旋转45度来模拟常见水印效果 watermark fitz.Point(center_x, center_y) page.insert_text( watermark, text, fontsizefont_size, fontnamehelv, color(1, 0, 0), # RGB红色 rotate45, overlayTrue # 作为覆盖层不会影响原有内容的选择 ) def merge_pdfs_with_watermark(pdf_list, output_filename, watermark_textNone): 合并PDF并为每一页添加水印 :param watermark_text: 水印文字如果为None则不添加 result_pdf fitz.open() for pdf_path in pdf_list: src_pdf fitz.open(pdf_path) # 插入源PDF的所有页面 result_pdf.insert_pdf(src_pdf) src_pdf.close() # 如果指定了水印文字为结果文档的每一页添加水印 if watermark_text: for page_num in range(len(result_pdf)): page result_pdf[page_num] add_watermark_to_page(page, watermark_text) # 添加水印后需要重新保存 # 注意这里我们直接修改了result_pdf保存即可 result_pdf.save(output_filename) result_pdf.close() print(f带水印合并完成文件已保存至{output_filename})这个例子展示了添加文本水印。你还可以用page.insert_image()来添加图片水印或者用更精细的Shape对象绘制复杂的图形和页眉页脚线。5. 工程化与实战打造命令行工具并集成到自动化流程一个脚本要真正成为“工具”就需要易用和可集成。我们将上面的功能整合并添加命令行接口使其可以通过终端直接调用。5.1 使用argparse构建命令行接口创建一个pdf_merger.py文件内容如下#!/usr/bin/env python3 PDF合并工具 - 命令行版本 支持文件夹扫描、排序、选择性合并、添加水印等功能。 import fitz import os import sys import argparse from pathlib import Path from typing import List, Optional def get_pdf_files_from_input(input_paths: List[str], sort_by: str) - List[Path]: 根据输入路径可能是文件或文件夹获取并排序PDF文件列表。 all_files [] for path_str in input_paths: path Path(path_str) if not path.exists(): print(f警告路径不存在已跳过 - {path_str}) continue if path.is_file() and path.suffix.lower() .pdf: all_files.append(path) elif path.is_dir(): all_files.extend(list(path.glob(*.pdf))) else: print(f警告忽略非PDF文件或无法识别的路径 - {path_str}) if not all_files: raise ValueError(未找到任何有效的PDF文件。) # 排序 if sort_by name: all_files.sort(keylambda x: x.name.lower()) elif sort_by mtime: all_files.sort(keylambda x: x.stat().st_mtime) elif sort_by ctime: all_files.sort(keylambda x: x.stat().st_ctime) else: # 默认按名称 all_files.sort(keylambda x: x.name.lower()) return all_files def merge_pdfs( file_list: List[Path], output: Path, page_ranges: Optional[List[tuple]] None, watermark: Optional[str] None ) - None: 执行合并的核心函数。 result_pdf fitz.open() if page_ranges: if len(page_ranges) ! len(file_list): raise ValueError(页面范围列表的长度必须与文件列表长度一致。) else: page_ranges [None] * len(file_list) for pdf_file, page_range in zip(file_list, page_ranges): try: src_pdf fitz.open(pdf_file) if page_range is None: result_pdf.insert_pdf(src_pdf) else: start, end page_range if 0 start end len(src_pdf): result_pdf.insert_pdf(src_pdf, from_pagestart, to_pageend-1) else: print(f警告文件 {pdf_file.name} 的页码范围 {page_range} 无效已跳过。) src_pdf.close() except Exception as e: print(f处理文件 {pdf_file} 时发生错误{e}已跳过。) continue # 添加水印 if watermark: for page_num in range(len(result_pdf)): page result_pdf[page_num] rect page.rect # 简单的水印实现 page.insert_text( fitz.Point(rect.width / 3, rect.height / 2), watermark, fontsize50, color(0.8, 0.8, 0.8), # 浅灰色 rotate30, overlayTrue ) result_pdf.save(output) result_pdf.close() def main(): parser argparse.ArgumentParser( description一个功能强大的PDF合并工具, formatter_classargparse.RawDescriptionHelpFormatter, epilog 使用示例 %(prog)s file1.pdf file2.pdf -o merged.pdf %(prog)s ./my_docs -o all_sorted.pdf -s mtime %(prog)s chap1.pdf chap2.pdf -o book.pdf -w DRAFT ) parser.add_argument( inputs, nargs, help输入项。可以是PDF文件路径也可以是包含PDF的文件夹路径。 ) parser.add_argument( -o, --output, requiredTrue, help合并后的输出PDF文件路径。 ) parser.add_argument( -s, --sort, choices[name, mtime, ctime], defaultname, help对找到的PDF文件进行排序的方式按文件名(name)、修改时间(mtime)、创建时间(ctime)。默认为name。 ) parser.add_argument( -w, --watermark, help为每一页添加指定的文本水印。 ) # 为了简化这里省略了复杂的页面范围解析。实际可以扩展例如使用“1:3,5:end”这样的语法。 args parser.parse_args() try: # 1. 获取并排序文件 pdf_files get_pdf_files_from_input(args.inputs, args.sort) print(f找到 {len(pdf_files)} 个PDF文件将按顺序合并) for f in pdf_files: print(f - {f.name}) # 2. 执行合并 output_path Path(args.output) # 确保输出目录存在 output_path.parent.mkdir(parentsTrue, exist_okTrue) merge_pdfs(pdf_files, output_path, watermarkargs.watermark) print(f\n✅ 合并成功输出文件{output_path.absolute()}) print(f 文件大小{output_path.stat().st_size / 1024:.2f} KB) except Exception as e: print(f\n❌ 合并过程中出现错误{e}, filesys.stderr) sys.exit(1) if __name__ __main__: main()现在你可以在命令行中这样使用它# 合并两个文件 python pdf_merger.py chapter1.pdf chapter2.pdf -o book.pdf # 合并一个文件夹下所有PDF按修改时间排序 python pdf_merger.py ./weekly_reports/ -o weekly_summary.pdf -s mtime # 合并文件并添加水印 python pdf_merger.py doc1.pdf doc2.pdf -o confidential.pdf -w INTERNAL USE ONLY5.2 集成到自动化工作流一个真实案例假设你有一个每日运行的数据分析任务它会生成一份以日期命名的PDF报告如report_20231027.pdf存放在./reports/目录下。每周五你需要将本周的5份报告合并成一份周报。你可以写一个简单的调度脚本比如结合系统的cron或Windows任务计划来自动完成这个任务# weekly_merge.py import subprocess from datetime import datetime, timedelta import os def generate_weekly_report(): # 计算上周五到本周四的日期范围假设报告按日期生成 today datetime.now() # 找到最近的周五如果今天就是周五则用今天 last_friday today - timedelta(days(today.weekday() - 4) % 7) start_date last_friday - timedelta(days6) # 上周五 end_date last_friday # 本周四 date_str start_date.strftime(%Y%m%d) _ end_date.strftime(%Y%m%d) output_filename f./weekly_reports/weekly_summary_{date_str}.pdf # 构建文件列表假设报告命名格式为 report_YYYYMMDD.pdf file_list [] current_date start_date while current_date end_date: file_path f./daily_reports/report_{current_date.strftime(%Y%m%d)}.pdf if os.path.exists(file_path): file_list.append(file_path) current_date timedelta(days1) if not file_list: print(本周没有找到任何日报文件。) return # 调用我们的命令行工具进行合并 # 注意这里需要确保 pdf_merger.py 在可访问的路径或者使用绝对路径 cmd [python, pdf_merger.py] file_list [-o, output_filename, -s, name] try: result subprocess.run(cmd, capture_outputTrue, textTrue, checkTrue) print(周报合并成功) print(result.stdout) # 这里可以添加后续操作比如发送邮件、上传到云存储等 except subprocess.CalledProcessError as e: print(周报合并失败) print(e.stderr) if __name__ __main__: generate_weekly_report()将这个脚本设置为每周五下午定时运行你就彻底从手动合并PDF的重复劳动中解放出来了。这就是自动化工具的价值——将你的时间留给更有创造性的工作。6. 避坑指南与性能优化从能用走向好用在实际使用中你可能会遇到一些预料之外的问题。下面是我在多个项目中总结出的常见“坑”及其解决方案。6.1 编码与路径问题跨平台的兼容性在Windows系统上路径分隔符是反斜杠\而在Linux/macOS上是正斜杠/。使用Python的pathlib.Path或os.path模块来处理路径可以避免这个问题。我们的脚本中已经使用了Path这是一个好习惯。另一个常见问题是文件名或路径中含有空格或特殊字符如中文。在构建命令行参数或直接传递字符串时确保路径被正确引用。subprocess.run会自动处理但如果你自己拼接字符串可能会出错。使用shlex.quote()或在构建列表时直接传递路径字符串而非拼接是更安全的方式。6.2 内存管理与大文件处理合并非常大的PDF文件比如数百MB的扫描件合集时可能会消耗大量内存。PyMuPDF本身效率很高但仍有优化空间。策略一增量合并与临时文件不要一次性将所有PDF内容读入内存再写入。虽然我们的脚本是循环插入但最终保存时所有数据仍在内存中。对于极端情况可以采用“增量保存”策略合并一部分保存到一个临时文件然后以这个临时文件为基础继续合并下一个。但这会带来IO开销仅在内存严重不足时考虑。策略二关闭与清理务必确保在每个fitz.open()之后都对应一个close()。使用with语句上下文管理器是更好的选择它能保证即使发生异常文件也会被正确关闭。# 更安全的写法 with fitz.open(pdf_path) as src_pdf: result_pdf.insert_pdf(src_pdf) # 退出with块后src_pdf自动关闭策略三监控与日志在生产环境中为长时间运行的合并任务添加日志和进度提示是很有必要的。你可以估算总页数在合并每个文件时打印进度。total_pages_estimated sum([fitz.open(f).page_count for f in pdf_files]) # 先快速统计总页数会打开关闭一次文件 pages_done 0 for pdf_path in pdf_files: with fitz.open(pdf_path) as src_pdf: old_count len(result_pdf) result_pdf.insert_pdf(src_pdf) pages_done (len(result_pdf) - old_count) print(f进度{pages_done}/{total_pages_estimated} 页 ({pages_done/total_pages_estimated:.1%}))6.3 合并后文档属性元数据的处理合并多个PDF后输出文件的标题、作者、主题等元数据Metadata来自哪里默认情况下PyMuPDF创建的新文档有空的元数据或者继承自第一个被插入的源文档。这可能导致信息混乱。一个好的实践是在合并完成后统一设置输出文档的元数据。def set_pdf_metadata(pdf_document, title, author, subject, keywords): 设置PDF的元数据。 meta { title: title, author: author, subject: subject, keywords: keywords, creator: My PDF Merger Tool, producer: PyMuPDF, } # 移除值为空的项 meta {k: v for k, v in meta.items() if v} pdf_document.set_metadata(meta) # 在保存result_pdf之前调用 set_pdf_metadata(result_pdf, titleWeekly Report Consolidation, authorAuto-Merger)6.4 处理“损坏”或非标准的PDF有时你会遇到一些“奇怪”的PDF它们可能由非标准工具生成或者部分损坏。PyMuPDF在打开这类文件时可能会抛出异常。尝试修复可以先用其他工具如Ghostscript的命令行工具gs尝试修复PDF再进行处理。但这超出了纯Python脚本的范围。容错处理在我们的合并函数中已经用try...except包裹了每个文件的处理过程。当一个文件出错时我们会跳过它并记录日志而不是让整个任务失败。这对于批处理任务至关重要。你可以根据异常类型如fitz.FileDataError提供更具体的错误信息。try: with fitz.open(pdf_path) as src_pdf: result_pdf.insert_pdf(src_pdf) except fitz.FileDataError as e: print(f错误文件 {pdf_path} 可能已损坏或不是有效的PDF。详情{e}) except Exception as e: print(f处理 {pdf_path} 时发生未知错误{e})7. 超越合并探索PDF处理的其他可能性当你掌握了PDF合并的核心技能后很容易将这些知识扩展到其他常见的PDF操作上因为底层库的API是相通的。这里简要提几个方向你可以基于现有的工具框架进行扩展。拆分PDF与合并相反使用Page对象将文档的特定页面提取出来保存为新的PDF。def split_pdf_by_pages(input_pdf, page_ranges, output_prefix): 将PDF按指定页码范围拆分成多个文件。 src_pdf fitz.open(input_pdf) for i, (start, end) in enumerate(page_ranges): new_pdf fitz.open() new_pdf.insert_pdf(src_pdf, from_pagestart, to_pageend-1) new_pdf.save(f{output_prefix}_part{i1}.pdf) new_pdf.close() src_pdf.close()提取文本和图片PyMuPDF可以非常精确地定位和提取页面上的文本块和图像这对于文档内容分析、数据抓取或存档非常有用。page doc[0] # 提取文本 text page.get_text() # 提取图片列表 image_list page.get_images() for img in image_list: # 处理图片...旋转页面批量调整扫描文档的方向。page.set_rotation(90) # 旋转90度添加链接和书签在合并后的文档中创建可点击的目录或内部链接提升阅读体验。将这些功能与你已经实现的合并工具结合你就能打造一个属于自己的、功能全面的PDF处理工具箱。无论是用于个人文档管理还是集成到公司的自动化流程中都能极大地提升效率。

相关新闻

Cursor Pro破解工具完整教程:三步实现永久免费AI编程

Cursor Pro破解工具完整教程:三步实现永久免费AI编程

Cursor Pro破解工具完整教程:三步实现永久免费AI编程 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reached your tria…

2026/10/14 15:38:57 阅读更多 →
STM32串口实时调节PWM参数:定时器配置与动态更新实战

STM32串口实时调节PWM参数:定时器配置与动态更新实战

1. 项目概述与核心价值最近在做一个电机调速的小项目,核心需求是通过上位机(比如电脑上的串口调试助手)实时调整下位机(STM32)输出的PWM波参数。这听起来是个基础功能,但实际做下来,你会发现它几…

2026/10/15 15:15:45 阅读更多 →
2026年微信小程序开店用哪个平台?费用、功能与开店流程对比

2026年微信小程序开店用哪个平台?费用、功能与开店流程对比

商家搜索“微信小程序开店用哪个平台”,通常希望在较短时间内完成商品上架、在线支付和会员运营。但不同平台面对的客户渠道不同,独立站工具、网站电商和原生小程序商城不能直接相互替代。客户主要在微信内时,应优先检查小程序支付、分享、会…

2026/10/15 15:15:51 阅读更多 →

最新新闻

水性醛酮树脂CT系列:提升漆膜光泽与硬度的功能助树脂

水性醛酮树脂CT系列:提升漆膜光泽与硬度的功能助树脂

做水性涂料这几年,我听得最多的反馈就三个:光泽差一点,硬度软一点,色浆放两天就返粗。这三个问题单拎出来都不算大,但放到工业漆、木器漆和油墨上,就是客户整批次退货的理由,严重一点直接丢订单…

2026/10/18 3:52:22 阅读更多 →
XERJ Autoindex + MCP 在 Windows 上的端到端实战:从安装、建索引到 Agent 检索的一站式闭环

XERJ Autoindex + MCP 在 Windows 上的端到端实战:从安装、建索引到 Agent 检索的一站式闭环

人工智能搜索引擎本地部署后端MCP 服务 【免费下载链接】xerj XERJ is the new way for AI to search data. Its autoindex capability activates agents to know your data without the token waste of grep and sed. One command indexes code, docs, logs and PDFs for sear…

2026/10/18 3:52:22 阅读更多 →
JavaScript原生四则运算计算器:从字符串拼接陷阱到精度控制

JavaScript原生四则运算计算器:从字符串拼接陷阱到精度控制

1. 这不是计算器Demo,而是前端交互逻辑的“最小可行验证”你有没有遇到过这样的场景:在表单里填两个数字,点个按钮,结果却卡在页面上不动?或者更糟——输入1和2,显示出来的却是"12"?这…

2026/10/18 3:52:22 阅读更多 →
JS高德地图设备图标实战:实时位置渲染与Marker管理优化

JS高德地图设备图标实战:实时位置渲染与Marker管理优化

1. 从一张“会说话的地图”说起:设备图标到底在解决什么问题做过物联网监控大屏、车辆调度后台或者共享设备管理平台的人,大概率都绕不开一个需求:把一堆设备的实时位置,用一张地图直观地铺出来。而“基于JS高德地图设置设备图标”…

2026/10/18 3:52:22 阅读更多 →
Unity协程底层原理与生命周期深度解析

Unity协程底层原理与生命周期深度解析

1. 协程不是“多线程”,但比多线程更难讲清楚刚接触 Unity 的开发者,十有八九会在协程(Coroutine)上栽第一个跟头。我见过太多人把StartCoroutine当成“开个后台线程”来用——结果发现 UI 还是卡、计时器不准、对象销毁后协程还在…

2026/10/18 3:52:22 阅读更多 →
从零跑通开源大模型:选型、环境、加载与推理全指南

从零跑通开源大模型:选型、环境、加载与推理全指南

很多人第一次看到“初始大模型”这个说法,第一反应是:是不是得先啃下一堆数学公式和论文?我在刚开始接触时也是这么想的,后来才发现,初始大模型这件事拆开来看,无非是两条线:一条是认知线&#…

2026/10/18 3:51:21 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →