使用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
相关产品推荐
相关产品推荐

