Back to skills
extension
Category: Productivity & OfficeNo API key required

智能PDF章节拆分专家

PDF-chapter-splitter

personAuthor: Nicole0808hubModelScope

Document Chapter Splitter 教材章节拆分工具

功能说明

此Skill用于处理PDF教材文档,将多章节教材按章节拆分为独立的PDF文件,便于后续学习、复习和分析。支持智能页码计算和练习题归属处理。

核心原则

格式保持

  • 输出格式保持不变:PDF文档拆分后仍为PDF
  • 保留原文格式设置:字体、字号、颜色、对齐方式等格式完整保留
  • 保留图表内容:图片、表格、图表等元素完整保留
  • 不破坏原文结构:段落、列表、引用等结构完整保留

内容完整性

  • 章节内容完整,无遗漏
  • 图表位置正确,无错位
  • 跨页内容正确衔接
  • 特殊元素(公式、脚注等)完整保留
  • 页码完全匹配:拆分后总页数 = 原PDF总页数

练习题处理规则

  • 练习题归入上一个章节
  • 不单独拆分练习题章节
  • 保持练习题与所属章节的完整性
  • 示例:第7章练习题放在第7章末尾,而不是第8章开头

两种页码计算方式

方式一:直接指定实际页码(推荐)

适用于:已知每个章节在PDF中的实际物理页码位置

chapters_config = [
    {"num": 1, "title": "章节名称", "start": 14},  # start为实际物理页码
    {"num": 2, "title": "章节名称", "start": 63},
    # ...
]

优点:精确控制,无需计算 适用场景:通过扫描PDF已确定每个章节的实际起始页

方式二:目录页码 + 偏移量

适用于:有完整目录,目录页码与实际页码有固定偏移

chapters_config = [
    {"num": 1, "title": "章节名称", "toc_page": 1},  # toc_page为目录中的页码
    {"num": 2, "title": "章节名称", "toc_page": 7},
    # ...
]

# 计算实际起始页
offset = 10  # 偏移量(通常是前言页数)
for ch in chapters_config:
    ch['start'] = ch['toc_page'] + offset

优点:配置简单,适合目录页码连续的情况 适用场景:目录页码与实际页码有固定差值

完整Python拆分脚本模板

import re
import sys
import os
from pypdf import PdfReader, PdfWriter

sys.stdout.reconfigure(encoding='utf-8')

# ============ 配置区域 ============
pdf_path = r"原PDF文件路径"
output_dir = r"输出目录路径"

# 章节配置(根据实际情况选择方式一或方式二)
chapters_config = [
    {"num": 1, "title": "第一章标题", "start": 14},  # 方式一:直接指定实际页码
    # 或
    {"num": 1, "title": "第一章标题", "toc_page": 1},  # 方式二:目录页码
    # ... 更多章节
]

# 练习题所在章节(这些章节包含练习题)
exercise_chapters = [7, 9, 12, 15]  # 根据实际情况调整

# 页码偏移量(仅方式二需要)
page_offset = 10  # 实际起始页 = 目录页码 + page_offset

# ============ 拆分逻辑 ============

reader = PdfReader(pdf_path)
total_pages = len(reader.pages)

print("="*100)
print("PDF章节拆分")
print("="*100)
print(f"\n原PDF总物理页数: {total_pages} 页\n")

# 方式二:计算实际起始页
if 'toc_page' in chapters_config[0]:
    for ch in chapters_config:
        ch['start'] = ch['toc_page'] + page_offset

# 计算每个章节的结束页
for i in range(len(chapters_config)):
    if i < len(chapters_config) - 1:
        chapters_config[i]['end'] = chapters_config[i + 1]['start'] - 1
    else:
        chapters_config[i]['end'] = total_pages

# 显示章节配置
print(f"✓ 共 {len(chapters_config)} 个章节")
print(f"\n章节边界配置:")
print(f"{'章节':<8} {'标题':<25} {'起始页':<8} {'结束页':<8} {'页数'}")
print("-"*80)

for ch in chapters_config:
    pages = ch['end'] - ch['start'] + 1
    exercise_note = " (+练习题)" if ch['num'] in exercise_chapters else ""
    print(f"第{ch['num']:<6}{ch['title']:<25} {ch['start']:<8} {ch['end']:<8} {pages}{exercise_note}")

# 计算前言页数
front_matter_pages = chapters_config[0]['start'] - 1
print(f"\n前言部分: 第1页 ~ 第{front_matter_pages}页 ({front_matter_pages}页)")

# 清理旧文件
print("\n" + "="*100)
print("清理旧文件并开始拆分...")
print("="*100 + "\n")

existing_files = [f for f in os.listdir(output_dir) if f.endswith('.pdf')]
for f in existing_files:
    try:
        os.remove(os.path.join(output_dir, f))
        print(f"已删除: {f}")
    except Exception as e:
        print(f"删除失败: {f}")

split_results = []

# 拆分前言部分
if front_matter_pages > 0:
    writer = PdfWriter()
    for page_idx in range(front_matter_pages):
        writer.add_page(reader.pages[page_idx])
    
    filename = "00_前言与目录.pdf"
    output_path = os.path.join(output_dir, filename)
    
    with open(output_path, 'wb') as output_file:
        writer.write(output_file)
    
    split_results.append({
        'filename': filename,
        'chapter_num': 0,
        'title': '前言与目录',
        'start_page': 1,
        'end_page': front_matter_pages,
        'page_count': front_matter_pages
    })
    
    print(f"✓ 已生成: {filename}")
    print(f"           文档页码: 第1页 ~ 第{front_matter_pages}页 (共{front_matter_pages}页)\n")

# 拆分各章节
for i, ch in enumerate(chapters_config):
    start_idx = ch['start'] - 1
    end_idx = ch['end'] - 1
    
    if start_idx >= total_pages:
        continue
    
    writer = PdfWriter()
    
    actual_end = min(end_idx, total_pages - 1)
    for page_idx in range(start_idx, actual_end + 1):
        writer.add_page(reader.pages[page_idx])
    
    safe_title = re.sub(r'[\\/:*?"<>|]', '_', ch['title'])
    filename = f"{ch['num']:02d}_{safe_title}.pdf"
    output_path = os.path.join(output_dir, filename)
    
    with open(output_path, 'wb') as output_file:
        writer.write(output_file)
    
    page_count = actual_end - start_idx + 1
    split_results.append({
        'filename': filename,
        'chapter_num': ch['num'],
        'title': ch['title'],
        'start_page': ch['start'],
        'end_page': actual_end + 1,
        'page_count': page_count
    })
    
    exercise_marker = " ✓含练习题" if ch['num'] in exercise_chapters else ""
    print(f"✓ 已生成: {filename}{exercise_marker}")
    print(f"           文档页码: 第{ch['start']}页 ~ 第{actual_end+1}页 (共{page_count}页)")

# 结果汇总
print(f"\n{'='*100}")
print("拆分完成! 最终结果汇总:")
print(f"{'='*100}")

total_split_pages = sum(r['page_count'] for r in split_results)

print(f"\n✓ 生成文件总数: {len(split_results)} 个")
print(f"✓ 原PDF总页数:  {total_pages} 页")
print(f"✓ 拆分后总页数: {total_split_pages} 页")
print(f"✓ 章节覆盖:     第1章 ~ 第{chapters_config[-1]['num']}章 (共{len(chapters_config)}章)")
print(f"✓ 页码匹配:     {'✓ 完全匹配' if total_split_pages == total_pages else f'⚠ 差异: {abs(total_pages - total_split_pages)}页'}")

# 显示完整文件列表
print(f"\n{'='*100}")
print(f"完整文件列表:")
print(f"{'='*100}")
print(f"\n{'序号':<4} {'文件名':<32} {'起始页':<8} {'结束页':<8} {'页数':<6} {'章节标题'}")
print("-"*95)

for i, r in enumerate(split_results, 1):
    ch_label = f"第{r['chapter_num']}章" if r['chapter_num'] > 0 else "前言"
    exercise_mark = "*" if r['chapter_num'] in exercise_chapters else ""
    print(f"{i:<4} {r['filename'][:31]:<32}{r['start_page']:<7}页 第{r['end_page']:<7}{r['page_count']:<6}{ch_label}: {r['title']}{exercise_mark}")

print(f"\n说明: * 标记表示该章节包含其练习题内容")
print(f"\n输出目录: {output_dir}")
print(f"{'='*100}")
print("🎉 PDF章节拆分完成!")
print(f"{'='*100}")

处理流程

Step 1: 确定拆分方案

  1. 用户提供PDF文件路径和输出目录
  2. 用户提供章节目录(图片或文字形式)
  3. 确定页码计算方式:
    • 方式一:用户提供每个章节的实际物理页码
    • 方式二:用户提供目录页码 + 偏移量(如:目录页码 + 10)

Step 2: 配置章节信息

根据用户提供的目录,配置 chapters_config

# 方式一示例(系统集成项目管理工程师)
chapters_config = [
    {"num": 1, "title": "信息化发展", "start": 14},
    {"num": 2, "title": "信息技术发展", "start": 63},
    {"num": 3, "title": "信息技术服务", "start": 108},
    # ... 更多章节
]

# 方式二示例(中级经济师)
chapters_config = [
    {"num": 1, "title": "社会主义基本经济制度", "toc_page": 1},
    {"num": 2, "title": "市场需求供给与均衡价格", "toc_page": 7},
    # ... 更多章节
]
page_offset = 10  # 实际起始页 = 目录页码 + 10

Step 3: 识别练习题归属

根据PDF内容,确定哪些章节包含练习题:

exercise_chapters = [7, 9, 12, 15]  # 这些章节的练习题归入本章节

Step 4: 生成并执行Python脚本

  1. 根据配置生成完整的Python拆分脚本
  2. 执行脚本进行拆分
  3. 验证拆分结果:
    • 检查文件数量 = 章节数 + 1(前言)
    • 检查总页数是否匹配
    • 检查练习题是否正确归属

Step 5: 处理特殊情况

如需将某个章节进一步拆分(如第36章拆分为第36章和第37章):

# 将第36章的第27页开始拆分为第37章
input_path = r"36_公司法律制度.pdf"
reader = PdfReader(input_path)

# 第36章:第1-26页
writer1 = PdfWriter()
for i in range(26):
    writer1.add_page(reader.pages[i])
# 保存为 36_公司法律制度.pdf

# 第37章:第27页到最后
writer2 = PdfWriter()
for i in range(26, len(reader.pages)):
    writer2.add_page(reader.pages[i])
# 保存为 37_新章节名称.pdf

文件命名规范

前言部分

  • 格式:00_前言与目录.pdf
  • 示例:00_前言与目录.pdf

章节部分

  • 格式:章节序号(两位数)_章节名称.pdf
  • 示例:
    • 01_信息化发展.pdf
    • 02_信息技术发展.pdf
    • 18_职业道德规范.pdf

特殊字符处理

章节名称中的特殊字符自动替换为下划线:

  • \ / : * ? " < > |_

验证检查清单

拆分完成后,必须验证以下内容:

  • [ ] 文件数量正确:生成文件数 = 章节数 + 1(前言)
  • [ ] 总页数匹配:拆分后总页数 = 原PDF总页数
  • [ ] 章节顺序正确:文件按章节序号排列
  • [ ] 练习题归属正确:练习题在所属章节末尾
  • [ ] 章节内容完整:每个章节包含完整内容
  • [ ] 格式保持完整:图表、表格、格式正确保留
  • [ ] 前言独立保存:前言部分单独保存为 00_前言与目录.pdf

使用场景

  • ✅ 处理教材、教程类PDF文档
  • ✅ 处理考试复习资料
  • ✅ 处理技术手册、规范文档
  • ✅ 需要将大型PDF拆分为可管理的小单元
  • ✅ 需要保持原文档格式和排版的场景
  • ✅ 需要按章节独立学习或复习的场景

注意事项

  1. 页码是物理页码:配置中的页码是PDF的实际物理页码(从1开始),不是文档中标注的页码
  2. 前言自动处理:第一个章节之前的页面自动保存为前言
  3. 练习题需明确:需要用户明确指出哪些章节包含练习题
  4. 页码必须连续:章节之间不能有间隙,下一章起始页 = 上一章结束页 + 1
  5. 验证总页数:拆分后必须验证总页数是否与原PDF一致
  6. 文件名唯一:章节名称不能包含特殊字符,会自动替换