You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PDFMinerLoader解析PDF时多余换行问题的解决咨询

PDFMinerLoader解析列表换行错位的解决办法

PDFMinerLoader出现列表编号/项目符号与内容拆分换行的问题,本质是PDF的文本布局被拆分成了独立的文本块,默认解析逻辑没有将同一行的编号和内容合并。以下是几个实用的解决思路:

1. 调整PDFMiner布局参数+文本后处理

先通过调整布局分析参数,让PDFMiner尽量减少不必要的文本块拆分,再对解析后的文本做针对性修复:

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO
import re

def extract_pdf_with_fixed_lists(pdf_path):
    # 初始化PDFMiner资源管理器和布局参数
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    # 调整布局参数,降低文本块拆分概率
    laparams = LAParams(
        char_margin=2.0,  # 增大字符间距阈值,同一行的字符更易合并
        word_margin=0.5,
        line_margin=0.5
    )
    device = TextConverter(rsrcmgr, retstr, laparams=laparams)
    
    # 解析PDF
    with open(pdf_path, 'rb') as fp:
        interpreter = PDFPageInterpreter(rsrcmgr, device)
        for page in PDFPage.get_pages(fp, check_extractable=True):
            interpreter.process_page(page)
    
    raw_text = retstr.getvalue()
    device.close()
    retstr.close()
    
    # 后处理:修复列表换行问题
    lines = raw_text.split('\n')
    fixed_lines = []
    i = 0
    # 匹配常见列表开头的正则
    list_item_pattern = re.compile(r'^\s*(\d+\.|[a-z]+\.|(\(\d+\))|●|○|-|\*)\s*$', re.IGNORECASE)
    
    while i < len(lines):
        current_line = lines[i].strip()
        # 检查当前行是否是列表项标识
        if list_item_pattern.match(current_line):
            # 如果下一行存在且不为空,合并两行
            if i + 1 < len(lines) and lines[i+1].strip():
                fixed_lines.append(f"{current_line} {lines[i+1].strip()}")
                i += 2
                continue
        fixed_lines.append(current_line)
        i += 1
    
    return '\n'.join(fixed_lines)

2. 重写LangChain的PDFMinerLoader(如果用LangChain)

如果是在LangChain框架中使用PDFMinerLoader,可以自定义Loader类,集成上述修复逻辑:

from langchain.document_loaders import PDFMinerLoader
from langchain.schema import Document
import re

class FixedPDFMinerLoader(PDFMinerLoader):
    def load(self) -> list[Document]:
        # 调用父类方法获取原始文档
        raw_docs = super().load()
        fixed_docs = []
        
        list_item_pattern = re.compile(r'^\s*(\d+\.|[a-z]+\.|(\(\d+\))|●|○|-|\*)\s*$', re.IGNORECASE)
        
        for doc in raw_docs:
            lines = doc.page_content.split('\n')
            fixed_lines = []
            i = 0
            
            while i < len(lines):
                current_line = lines[i].strip()
                if list_item_pattern.match(current_line):
                    if i + 1 < len(lines) and lines[i+1].strip():
                        fixed_lines.append(f"{current_line} {lines[i+1].strip()}")
                        i += 2
                        continue
                fixed_lines.append(current_line)
                i += 1
            
            # 替换修复后的内容,保留原文档的元数据
            fixed_doc = doc.copy()
            fixed_doc.page_content = '\n'.join(fixed_lines)
            fixed_docs.append(fixed_doc)
        
        return fixed_docs

使用时直接替换原PDFMinerLoader即可:

loader = FixedPDFMinerLoader("your_file.pdf")
documents = loader.load()

3. 灵活调整规则适配不同PDF

不同PDF的排版差异大,可以根据实际情况修改正则表达式,比如匹配更多类型的列表符号(如罗马数字、特殊项目符号),或者调整LAParams的参数:

  • char_margin:字符间的最大间距,超过则拆分成不同块,数值越大越不容易拆分
  • line_margin:行与行之间的最小间距,小于则合并成同一行
  • word_margin:单词间的最大间距,超过则拆分成不同块

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:07:05