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: 确定拆分方案
- 用户提供PDF文件路径和输出目录
- 用户提供章节目录(图片或文字形式)
- 确定页码计算方式:
- 方式一:用户提供每个章节的实际物理页码
- 方式二:用户提供目录页码 + 偏移量(如:目录页码 + 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脚本
- 根据配置生成完整的Python拆分脚本
- 执行脚本进行拆分
- 验证拆分结果:
- 检查文件数量 = 章节数 + 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_信息化发展.pdf02_信息技术发展.pdf18_职业道德规范.pdf
特殊字符处理
章节名称中的特殊字符自动替换为下划线:
\ / : * ? " < > |→_
验证检查清单
拆分完成后,必须验证以下内容:
- [ ] 文件数量正确:生成文件数 = 章节数 + 1(前言)
- [ ] 总页数匹配:拆分后总页数 = 原PDF总页数
- [ ] 章节顺序正确:文件按章节序号排列
- [ ] 练习题归属正确:练习题在所属章节末尾
- [ ] 章节内容完整:每个章节包含完整内容
- [ ] 格式保持完整:图表、表格、格式正确保留
- [ ] 前言独立保存:前言部分单独保存为
00_前言与目录.pdf
使用场景
- ✅ 处理教材、教程类PDF文档
- ✅ 处理考试复习资料
- ✅ 处理技术手册、规范文档
- ✅ 需要将大型PDF拆分为可管理的小单元
- ✅ 需要保持原文档格式和排版的场景
- ✅ 需要按章节独立学习或复习的场景
注意事项
- 页码是物理页码:配置中的页码是PDF的实际物理页码(从1开始),不是文档中标注的页码
- 前言自动处理:第一个章节之前的页面自动保存为前言
- 练习题需明确:需要用户明确指出哪些章节包含练习题
- 页码必须连续:章节之间不能有间隙,下一章起始页 = 上一章结束页 + 1
- 验证总页数:拆分后必须验证总页数是否与原PDF一致
- 文件名唯一:章节名称不能包含特殊字符,会自动替换
Scan to join WeChat group