脚本专家 发表于 昨天 14:00

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文件,只需在外层加一个文件遍历循环即可。

热心网友4 发表于 昨天 19:00

Re: Python按页数拆分PDF并提取指定范围内容的实现方法

感谢分享,正好手头有份几百页的PDF需要按章节拆,这个思路很实用。看了下代码,方案一到方案三都明白了,不过标题里提到了按书签拆分,但代码好像只到方案三分块拆分就结束了,是不是没贴完?方便补一下第四种的实现吗?另外问一下,提取指定页码范围时如果起始页大于结束页,目前好像没做这个校验,实际用的时候可能容易出现边界情况。

热心网友4 发表于 昨天 19:00

Re: Python按页数拆分PDF并提取指定范围内容的实现方法

楼主这个拆分思路很实用,正好最近在整理技术文档,每次用阅读器手动抽页面真的痛苦,你这个脚本可以直接搞定。方桉2和方桉3的命名逻辑也很清楚,输出文件名直接带着页码范围,方便后面归档。 另外想请教下,如果源PDF有加密需要密码,pypdf直接读会报错吗?还有方桉4按书签拆分,我看代码好像没贴完,额外装pdfplumber是为了解析那种书签层级不规范的情况吗?期待楼主补充一下,谢谢分享!

热心网友7 发表于 昨天 19:10

Re: Python按页数拆分PDF并提取指定范围内容的实现方法

看到这个帖子真的挺有用的,最近刚好在处理一份几百页的PDF,正愁怎么按章节拆开分发呢。代码思路很清晰,从指定范围到逐页、分块都覆盖了,直接复制就能用。而且不重新渲染这点很关键,速度快还不损伤内容。期待看到方案4按书签拆分的实现,那个对按章节整理材料应该最方便。
页: [1]
查看完整版本: Python按页数拆分PDF并提取指定范围内容的实现方法