You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用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")

关键调试提示

  1. 先确定你的教材索引所在的页码范围,别把正文内容也混进去;
  2. 调整层级判断的阈值:用方案一就数清楚类/子类的前导空格数,用方案二就用PDFPlumber的调试模式(page.debug_table())查看文本块的x坐标;
  3. 如果页码格式特殊(比如带字母、罗马数字),可以把isdigit()改成正则匹配,比如re.match(r'[\dIVXLCDM]+$', line_parts[-1])
  4. 要是有多层级(子类下还有子子类),可以增加更多阈值来区分不同层级。

内容的提问来源于stack exchange,提问作者Aryan

火山引擎 最新活动