Python按页数拆分PDF并提取指定范围内容的实现方法
在日常办公和开发任务中,PDF按页拆分是一个非常常见的需求:从200页的培训教材里抽取15到30页,或者将合同按章节拆成独立文件分发给不同负责人。手动在阅读器里选择页面再导出的方式效率很低,而且每次只能处理一段范围。用Python脚本可以直接完成指定页码提取、逐页拆分、按块大小拆分以及按书签拆分为章节,适合批量处理和自动化流程。实现思路并不复杂:通过pypdf库的PdfReader读取源PDF页面,再用PdfWriter将选中的页面写入新的PDF文件。整个过程只涉及页面对象的复用和写入操作,不涉及重新渲染,所以速度很快,也不会损失原始PDF内容。
环境准备方面,需要安装pypdf,可以执行以下命令:
pip install pypdf
如果还需要解析非标准书签,可以额外安装pdfplumber:
pip install pdfplumber
下面给出完整实现代码,包含四种拆分方式:提取指定页码范围、逐页拆分、按固定页数分块、按书签章节拆分。
import os
from pypdf import PdfReader, PdfWriter
from pathlib import Path
# ==================== 方案 1:提取指定页码范围 ====================
def extract_pages(input_pdf, start_page, end_page, output_file):
"""
从 PDF 中提取指定范围的页面
参数:
input_pdf: 源 PDF 文件
start_page: 起始页码(从 1 开始)
end_page: 结束页码(包含)
output_file: 输出文件名
"""
reader = PdfReader(input_pdf)
total = len(reader.pages)
# 验证页码范围
if start_page < 1 or end_page > total:
print(f"错误: 文件共 {total} 页,请求范围 {start_page}-{end_page}")
return
writer = PdfWriter()
for i in range(start_page - 1, end_page):# 转为 0-based 索引
writer.add_page(reader.pages)
with open(output_file, 'wb') as f:
writer.write(f)
print(f"已提取第 {start_page}-{end_page} 页 → {output_file}")
# ==================== 方案 2:逐页拆分 ====================
def split_pdf_by_page(input_pdf, output_dir="逐页拆分"):
"""
将 PDF 的每一页拆分为独立文件
"""
reader = PdfReader(input_pdf)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
base_name = Path(input_pdf).stem
for i, page in enumerate(reader.pages):
writer = PdfWriter()
writer.add_page(page)
output_file = output_path / f"{base_name}_第{i+1}页.pdf"
with open(output_file, 'wb') as f:
writer.write(f)
print(f"逐页拆分完成: {len(reader.pages)} 页 → {output_dir}/")
# ==================== 方案 3:按指定块大小拆分 ====================
def split_pdf_by_chunks(input_pdf, chunk_size, output_dir="分块拆分"):
"""
按指定页数拆分(如每 10 页一个文件)
参数:
input_pdf: 源 PDF
chunk_size: 每个文件的页数
output_dir: 输出目录
"""
reader = PdfReader(input_pdf)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
total = len(reader.pages)
base_name = Path(input_pdf).stem
chunk_count = 0
for start in range(0, total, chunk_size):
end = min(start + chunk_size, total)
writer = PdfWriter()
for i in range(start, end):
writer.add_page(reader.pages)
output_file = output_path / f"{base_name}_第{start+1}-{end}页.pdf"
with open(output_file, 'wb') as f:
writer.write(f)
chunk_count += 1
print(f"已拆分: 第 {start+1}-{end} 页")
print(f"\n分块拆分完成: {chunk_count} 个文件")
# ==================== 方案 4:按章节/书签拆分 ====================
def split_pdf_by_outline(input_pdf, output_dir="按章节拆分"):
"""
按 PDF 的书签(大纲)拆分为独立章节文件
"""
reader = PdfReader(input_pdf)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
base_name = Path(input_pdf).stem
outline = reader.outline
# 提取书签信息
bookmarks = []
for item in outline:
if isinstance(item, dict) and '/Title' in item:
title = item['/Title']
page_num = None
if '/Dest' in item:
dest = item['/Dest']
if isinstance(dest, list) and dest:
page_num = reader.get_destination_page_number(item)
bookmarks.append({'title': title, 'page': page_num})
if not bookmarks:
print("该 PDF 没有书签/大纲")
return
# 按书签拆分
for idx, bm in enumerate(bookmarks):
if bm['page'] is None:
continue
start = bm['page']
# 下一个书签的页码 - 1 作为结束
if idx + 1 < len(bookmarks) and bookmarks['page'] is not None:
end = bookmarks['page'] - 1
else:
end = len(reader.pages) - 1
writer = PdfWriter()
for i in range(start, end + 1):
writer.add_page(reader.pages)
safe_title = bm['title'].replace('/', '_').replace('\\', '_')
output_file = output_path / f"{safe_title}.pdf"
with open(output_file, 'wb') as f:
writer.write(f)
print(f"已拆分章节: {bm['title']} (第 {start+1}-{end+1} 页)")
print(f"\n按章节拆分完成: {output_dir}/")
# ==================== 使用示例 ====================
if __name__ == "__main__":
PDF_FILE = "培训教材_200页.pdf"
# 方案 1:提取第 15-30 页
extract_pages(PDF_FILE, 15, 30, "培训教材_核心章节.pdf")
# 方案 2:逐页拆分
# split_pdf_by_page(PDF_FILE)
# 方案 3:每 10 页一个文件
# split_pdf_by_chunks(PDF_FILE, chunk_size=10)
# 方案 4:按书签拆分
# split_pdf_by_outline(PDF_FILE)
代码说明:extract_pages函数的start_page和end_page均从1开始计数,内部通过start_page-1转换为pypdf的0基索引。输出前会校验页码范围,避免越界。逐页拆分会在指定目录下生成“原文件名_第N页.pdf”的独立文件。分块拆分需要传入chunk_size参数,例如chunk_size=10表示每10页生成一个文件,最后一个文件可能不足10页。按书签拆分则依赖PDF内置大纲,提取出章节标题和对应起始页码,并把每个书签到下一个书签之间的页面归入同一章节;标题中的特殊字符会被替换为下划线,避免生成非法文件名。
实际使用中可能遇到两个典型问题。
问题一:提取后页面空白。这通常出现在扫描件或加密PDF上,pypdf无法直接读取加密内容。可以在创建reader之后先判断并解密:
reader = PdfReader(input_pdf)
if reader.is_encrypted:
reader.decrypt("")
空字符串解密适用于无密码的加密文件;如果PDF本身有打开密码,则需要传入对应密码。
问题二:按书签拆分时没有提取到书签。部分PDF的书签可能存储在非标准位置,pypdf的outline属性无法读取。此时可以改用pdfplumber尝试获取更完整的目录信息:
import pdfplumber
with pdfplumber.open(pdf_file) as pdf:
toc = pdf.toc
print(toc)
pdfplumber会尽量解析文档结构中的书签数据,拿到目录后可以再结合页码信息自行实现拆分逻辑。
总结一下四种方式的特点:
拆分方式 函数 说明
指定范围 extract_pages() 提取 start-end 页
逐页拆分 split_pdf_by_page() 每页一个文件
分块拆分 split_pdf_by_chunks() 每 N 页一个文件
按书签拆分 split_pdf_by_outline() 按章节拆分
如果你的业务场景是“只取中间某一段”,用extract_pages最直接;如果需要将整份文档拆成多个固定页数的小文件,split_pdf_by_chunks更合适;而按书签拆分则适合结构清晰的电子书或技术手册,可以一次性得到按章节组织的独立PDF。这套逻辑同样可以扩展到批量处理多个PDF文件,只需在外层加一个文件遍历循环即可。
Re: Python按页数拆分PDF并提取指定范围内容的实现方法
感谢分享,正好手头有份几百页的PDF需要按章节拆,这个思路很实用。看了下代码,方案一到方案三都明白了,不过标题里提到了按书签拆分,但代码好像只到方案三分块拆分就结束了,是不是没贴完?方便补一下第四种的实现吗?另外问一下,提取指定页码范围时如果起始页大于结束页,目前好像没做这个校验,实际用的时候可能容易出现边界情况。Re: Python按页数拆分PDF并提取指定范围内容的实现方法
楼主这个拆分思路很实用,正好最近在整理技术文档,每次用阅读器手动抽页面真的痛苦,你这个脚本可以直接搞定。方桉2和方桉3的命名逻辑也很清楚,输出文件名直接带着页码范围,方便后面归档。 另外想请教下,如果源PDF有加密需要密码,pypdf直接读会报错吗?还有方桉4按书签拆分,我看代码好像没贴完,额外装pdfplumber是为了解析那种书签层级不规范的情况吗?期待楼主补充一下,谢谢分享!Re: Python按页数拆分PDF并提取指定范围内容的实现方法
看到这个帖子真的挺有用的,最近刚好在处理一份几百页的PDF,正愁怎么按章节拆开分发呢。代码思路很清晰,从指定范围到逐页、分块都覆盖了,直接复制就能用。而且不重新渲染这点很关键,速度快还不损伤内容。期待看到方案4按书签拆分的实现,那个对按章节整理材料应该最方便。
页:
[1]