如何用Python提取PDF书籍的章节目录(含子标题)
Python提取PDF结构化目录(含章节及子标题)
一、提取PDF内置书签(Outline)(优先推荐)
正规出版的PDF书籍大多会内置结构化书签目录,这是最准确的提取方式。PyMuPDF(fitz)支持直接获取书签的层级关系,之前可能没用到这个核心功能。
import fitz # PyMuPDF def extract_outline(pdf_path): doc = fitz.open(pdf_path) # 获取书签列表,格式为[(层级, 标题, 页码), ...] outline = doc.get_toc() doc.close() # 格式化层级结构输出 formatted_toc = [] for level, title, page in outline: indent = " " * (level - 1) formatted_toc.append(f"{indent}{level}. {title} (页码: {page})") return "\n".join(formatted_toc) # 使用示例 pdf_path = "your_book.pdf" print(extract_outline(pdf_path))
说明:get_toc()返回的每个元素第一个值就是层级(1为一级章节,2为子标题,以此类推),通过缩进可直观展示目录结构。
二、无内置书签时,从页面文本提取目录
如果PDF没有内置书签,只能从目录页文本中识别,核心是利用文本的字体大小、缩进位置区分章节层级。
import fitz def extract_toc_from_page(pdf_path, toc_page_numbers): doc = fitz.open(pdf_path) toc_items = [] for page_num in toc_page_numbers: # PyMuPDF页码从0开始,需减1 page = doc.load_page(page_num - 1) # 获取带格式的文本块信息 blocks = page.get_text("dict")["blocks"] for block in blocks: if "lines" not in block: continue for line in block["lines"]: # 提取该行的核心特征 font_size = line["spans"][0]["size"] left_offset = line["bbox"][0] text = "".join([span["text"].strip() for span in line["spans"]]) if not text: continue # 根据实际PDF格式调整层级判断阈值 if font_size >= 14: level = 1 elif font_size >= 12 and left_offset > 20: level = 2 elif font_size >= 10 and left_offset > 40: level = 3 else: continue # 跳过非目录文本 toc_items.append((level, text)) doc.close() # 格式化输出 formatted_toc = [] for level, title in toc_items: indent = " " * (level - 1) formatted_toc.append(f"{indent}{level}. {title}") return "\n".join(formatted_toc) # 使用示例:假设目录在第3、4页 pdf_path = "your_book.pdf" print(extract_toc_from_page(pdf_path, [3,4]))
说明:
- 通过
get_text("dict")获取文本的字体大小、位置坐标等元数据,是区分层级的关键。 - 需根据目标PDF的实际排版调整字体大小和缩进的阈值(比如有的一级标题用16号字,子标题缩进30px等)。
- 可额外添加正则匹配逻辑,提取标题后的页码信息,完善目录内容。
补充说明
- 之前用PyPDF2/PyMuPDF失败,大概率是只提取了纯文本,没利用内置书签或文本格式特征。
- 如果是扫描版PDF(图片格式),需先用
pytesseract做OCR识别,再用上述方法处理。
内容的提问来源于stack exchange,提问作者Mido Gimo
相关产品推荐
相关产品推荐

