使用Python解析PDF教材索引页:带缩进文本转CSV及层级拆分问题
解决PDF索引页文本提取与层级CSV拆分的问题
我之前处理PDF索引提取的时候也踩过Tika缩进的坑,它对非标准空格或者定位式缩进的识别确实拉胯。给你两个亲测有效的方案,能搞定层级拆分和页码关联:
方案一:PyPDF2 + 缩进空格分析
如果你的PDF索引是用空格缩进来区分层级的,PyPDF2提取文本时大概率能保留原始空格结构,然后我们可以通过统计每行的前导空格数来判断类/子类层级:
import PyPDF2 import csv def parse_index_to_csv(pdf_path, output_csv): # 读取PDF并提取索引页文本 with open(pdf_path, 'rb') as pdf_file: reader = PyPDF2.PdfReader(pdf_file) # 假设索引在最后3页,可根据实际教材调整页码范围 index_content = [] for page in reader.pages[-3:]: lines = page.extract_text().split('\n') # 过滤空行和无效行 index_content.extend([line for line in lines if line.strip()]) # 解析层级并写入CSV current_category = "" with open(output_csv, 'w', newline='', encoding='utf-8') as csv_file: writer = csv.writer(csv_file) writer.writerow(["类", "子类", "页码"]) for line in index_content: # 计算前导空格数 leading_spaces = len(line) - len(line.lstrip()) # 拆分主题和页码(假设页码在行尾为数字) line_parts = line.split() if not line_parts[-1].isdigit(): continue page_number = line_parts[-1] topic = ' '.join(line_parts[:-1]).strip() # 根据空格数判断层级,阈值需根据你的PDF调整 if leading_spaces <= 2: # 类层级 current_category = topic else: # 子类层级 writer.writerow([current_category, topic, page_number]) # 调用示例 parse_index_to_csv("your_textbook.pdf", "index_result.csv")
方案二:PDFPlumber + 文本位置定位
如果你的PDF索引是用固定位置缩进(不是空格,而是文本块的x坐标偏移),Tika完全识别不出来这种结构,但PDFPlumber可以精准获取每个文本块的位置信息,通过x坐标来区分层级:
import pdfplumber import csv def parse_index_with_position(pdf_path, output_csv): with pdfplumber.open(pdf_path) as pdf: # 指定索引页范围 index_pages = pdf.pages[-3:] current_category = "" # 自定义x坐标阈值,需要打开PDF查看实际类/子类的起始x值 category_x_threshold = 45 subcategory_x_threshold = 65 with open(output_csv, 'w', newline='', encoding='utf-8') as csv_file: writer = csv.writer(csv_file) writer.writerow(["类", "子类", "页码"]) for page in index_pages: # 按行提取文本块,保留位置信息 for text_line in page.extract_text_lines(): line_text = text_line['text'].strip() if not line_text: continue # 拆分主题和页码 line_parts = line_text.split() if not line_parts[-1].isdigit(): continue page_number = line_parts[-1] topic = ' '.join(line_parts[:-1]).strip() # 根据x坐标判断层级 if text_line['x0'] < category_x_threshold: current_category = topic elif text_line['x0'] >= subcategory_x_threshold: writer.writerow([current_category, topic, page_number]) # 调用示例 parse_index_with_position("your_textbook.pdf", "index_result.csv")
关键调试提示
- 先确定你的教材索引所在的页码范围,别把正文内容也混进去;
- 调整层级判断的阈值:用方案一就数清楚类/子类的前导空格数,用方案二就用PDFPlumber的调试模式(
page.debug_table())查看文本块的x坐标; - 如果页码格式特殊(比如带字母、罗马数字),可以把
isdigit()改成正则匹配,比如re.match(r'[\dIVXLCDM]+$', line_parts[-1]); - 要是有多层级(子类下还有子子类),可以增加更多阈值来区分不同层级。
内容的提问来源于stack exchange,提问作者Aryan




