PDF

导图一览:

工具名称功能技术输出格式特点
PyPDF 提取文本、图像和元数据 基于规则 (RB) TXT 成熟的纯 Python 库,处理多种 PDF 操作
pdfminer.six 提取文本、图像、目录、字体大小 基于规则 TXT、HTML、hOCR、JPG 多功能,支持 CJK 语言和垂直书写
PDFPlumber 提取文本和表格 基于规则 (基于 pdfminer) TXT、HTML、hOCR、JPG 提供可视化调试工具,提取过程便捷
PyMuPDF 提取文本、表格和图像 基于规则 (MuPDF),可选 OCR TXT、HTML、SVG、JSON Python 绑定,处理复杂文档布局
pypdfium2 提取文本 基于规则 TXT 轻量级库,专注文本提取
Unstructured 预处理和摄取图像及文本文档 基于规则,支持 OCR TXT 支持元素级文本和表格提取
Tabula 提取表格 基于规则 DataFrame、CSV、JSON Python 包装器,使用 tabula-java
Camelot 提取表格 基于规则 DataFrame、CSV、JSON、HTML 灵活配置,支持流模式和格子模式
Nougat 提取文本 基于 Transformer Markdown 深度学习模型,专为学术文档训练
Table Transformer (TATR) 检测表格 基于 Transformer 图像 对象检测模型,训练于 PubTables-1M 等

 

 

 

 

 

 

 

 

 

pdfplumber

pdfplumber库按页处理 pdf ,获取页面文字,提取表格等操作。

学习文档:

import pdfplumber
with pdfplumber.open("E:\新员工\【学员讲义】企业文化.pdf") as pdf:
    page01 = pdf.pages[9] #指定页码
    text = page01.extract_text()#提取文本
    print(text)

    table = page01.extract_tables()  # 提取表格
    print(table)

优势:

1. pdfplumber能轻松访问有关PDF对象的所有详细信息,且用于提取文本和表格的方法高级可定制,使用者可根据表格的具体形式来调整参数。

2. 最关键的是pdfplumber作者持续在维护该库,而同样受欢迎的PyPDF2已经不再维护了。

劣势:

1. 提取的 英文 中间没有空格

 

PyPDF2 

PyPDF2 是一个纯 Python PDF 库,可以读取文档信息(标题,作者等)、写入、分割、合并PDF文档,它还可以对pdf文档进行添加水印、加密解密等。

官方文档:          https://www.w3cschool.cn/pypdf2/

import PyPDF2
def extract_text_from_pdf(pdf_path):
    with open(pdf_path, 'rb') as file:
        pdf_reader = PyPDF2.PdfFileReader(file)
        num_pages = pdf_reader.numPages
        text = ""
        for page_num in range(num_pages):
            page = pdf_reader.getPage(page_num)
            text += page.extractText()
    return text

pdf_path = 'example.pdf'
extracted_text = extract_text_from_pdf(pdf_path)
print(extracted_text)
View Code

版本 2.6.0

劣势:

1.英文识别不行,识别成单个字母了 S o l a r

2.表格无法识别

 

pdf2docx

可将 PDF 转换成 docx 文件的 Python 库。

该项目通过 PyMuPDF 库提取 PDF 文件中的数据,然后采用 python-docx 库解析内容的布局、段落、图片、表格等,最后自动生成 docx 文件。

from pdf2docx import parse

pdf_file = 'E:\新员工\【学员讲义】企业文化.pdf'
docx_file = 'E:\新员工\【学员讲义】企业文化2.docx'

# convert pdf to docx
parse(pdf_file, docx_file)

 

PDFminer

PDFMiner内置pdf2txt.py和dumppdf.py。但是pdf2txt.py从PDF文件中提取所有文本内容。但不能识别画成图片的文本,这需要对图片特征进行识别。对于加密的PDF你需要提供一个密码才能解析,对于没有提取权限的PDF文档你得不到任何文本。

https://pdfminersix.readthedocs.io

对于每个LTPage对象,它从上到下遍历每个元素,并尝试将适当的组件识别为:

  • LTFigure:表示PDF中页面上的图形或图像的区域。
  • LTTextContainer:代表一个矩形区域(段落)中的一组文本行(line),然后进一步分析成LTTextLine对象的列表。它们中的每一个都表示一个LTChar对象列表,这些对象存储文本的单个字符及其元数据
  • LTRect表示一个二维矩形,可用于在LTPage对象中占位区或者Panel,图形或创建

因此,使用Python对页面进行重构之后,将页面元素分类为LTFigure(图像或图形)、LTTextContainer(文本信息)或LTRect(表格)

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

# 打开PDF文件
with open('example.pdf', 'rb') as file:
    # 创建一个PDFResourceManager对象
    resource_manager = PDFResourceManager()
    # 创建一个StringIO对象,用于存储提取的文本内容
    output = StringIO()
    # 创建一个TextConverter对象
    converter = TextConverter(resource_manager, output, laparams=LAParams())
    # 创建一个PDFPageInterpreter对象
    interpreter = PDFPageInterpreter(resource_manager, converter)
    # 逐页解析文档
    for page in PDFPage.get_pages(file):
        interpreter.process_page(page)
    # 获取提取的文本内容
    text = output.getvalue()
    print(text)
View Code
from pdfminer.high_level import extract_text
extracted_text = extract_text(pdf)
print(extracted_text)
View Code

 

Pdfminer.six

Pdfminer.six 是PDFMiner的一个分支。

它是一种从PDF中解析、提取信息的工具文件,侧重于获取和分析文本数据、元数据和图片,还可用于获取文本的确切位置、字体或颜色。Pdfminer.six 直接从页面中提取文本 PDF 的源代码。以模块化的方式构建,因此pdfminer.six的每个组件都可以轻松更换。

官方文档:https://pdfminersix.readthedocs.io/en/latest/index.html

代码参考:https://mp.weixin.qq.com/s/5OQGOT1rllEAE8_L-v4L9A

大概试了下,功能没那么强大

 

pymupdf

import fitz

def MuPDF_extract_text_from_pdf(path):
    doc = fitz.open(path)
    all_content = []
    page_nums = 0
    for i in doc.pages():
        page_nums += 1
        all_content.append(i.get_text())
    text = '\n'.join(all_content)
    # text = ''.join(text.split('\n'))
    return text
View Code

 

papermerge

from papermage.recipes import CoreRecipe

recipe = CoreRecipe()
doc = recipe.run("example.pdf")
for page in doc.pages:
    for row in page.rows:
        print(row.text)

 

xpdf

这个文本介绍了Xpdf,一个免费的PDF浏览器和工具包。它包括文本提取器、图片转换器、HTML转换器等工具,大部分工具都是开源的。

http://www.xpdfreader.com/

 

img2table

https://mp.weixin.qq.com/s/kPxiw4sgrr8Z3dHUt60dyw

 

综合运用上述模块

https://mp.weixin.qq.com/s/4mg59Sb7TzaoXVctEMJVWw  使用python提取PDF中的文本信息(包括表格和图片OCR) 

# 读取PDF
import PyPDF2
# 分析PDF的layout,提取文本
from pdfminer.high_level import extract_pages, extract_text
from pdfminer.layout import LTTextContainer, LTChar, LTRect, LTFigure
# 从PDF的表格中提取文本
import pdfplumber
# 从PDF中提取图片
from PIL import Image
from pdf2image import convert_from_path
# 运行OCR从图片中提取文本
import pytesseract
# 清除过程中的各种过程文件
import os

# 创建一个从pdf中裁剪图像元素的函数
def crop_image(element, pageObj):
    # 获取从PDF中裁剪图像的坐标
    [image_left, image_top, image_right, image_bottom] = [element.x0,element.y0,element.x1,element.y1]
    # 使用坐标(left, bottom, right, top)裁剪页面
    pageObj.mediabox.lower_left = (image_left, image_bottom)
    pageObj.mediabox.upper_right = (image_right, image_top)
    # 将裁剪后的页面保存为新的PDF
    cropped_pdf_writer = PyPDF2.PdfWriter()
    cropped_pdf_writer.add_page(pageObj)
    # 将裁剪好的PDF保存到一个新文件
    with open('cropped_image.pdf', 'wb') as cropped_pdf_file:
        cropped_pdf_writer.write(cropped_pdf_file)

# 创建一个将PDF内容转换为image的函数
def convert_to_images(input_file,):
    images = convert_from_path(input_file)
    image = images[0]
    output_file = "PDF_image.png"
    image.save(output_file, "PNG")

# 创建从图片中提取文本的函数
def image_to_text(image_path):
    # 读取图片
    img = Image.open(image_path)
    # 从图片中抽取文本
    text = pytesseract.image_to_string(img)
    return text

# 从页面中提取表格内容

def extract_table(pdf_path, page_num, table_num):
    # 打开PDF文件
    pdf = pdfplumber.open(pdf_path)
    # 查找已检查的页面
    table_page = pdf.pages[page_num]
    # 提取适当的表格
    table = table_page.extract_tables()[table_num]
    return table

# 将表格转换为适当的格式
def table_converter(table):
    table_string = ''
    # 遍历表格的每一行
    for row_num in range(len(table)):
        row = table[row_num]
        # 从warp的文字删除线路断路器
        cleaned_row = [item.replace('\n', ' ') if item is not None and '\n' in item else 'None' if item is None else item for item in row]
        # 将表格转换为字符串,注意'|'、'\n'
        table_string+=('|'+'|'.join(cleaned_row)+'|'+'\n')
    # 删除最后一个换行符
    table_string = table_string[:-1]
    return table_string


# 创建一个文本提取函数

def text_extraction(element):
    # 从行元素中提取文本
    line_text = element.get_text()

    # 探析文本的格式
    # 用文本行中出现的所有格式初始化列表
    line_formats = []
    for text_line in element:
        if isinstance(text_line, LTTextContainer):
            # 遍历文本行中的每个字符
            for character in text_line:
                if isinstance(character, LTChar):
                    # 追加字符的font-family
                    line_formats.append(character.fontname)
                    # 追加字符的font-size
                    line_formats.append(character.size)
    # 找到行中唯一的字体大小和名称
    format_per_line = list(set(line_formats))

    # 返回包含每行文本及其格式的元组
    return (line_text, format_per_line)

# 查找PDF路径
pdf_path = r'E:\5.pdf'


# 创建一个PDF文件对象
pdfFileObj = open(pdf_path, 'rb')
# 创建一个PDF阅读器对象
pdfReaded = PyPDF2.PdfReader(pdfFileObj)

# 创建字典以从每个图像中提取文本
text_per_page = {}
# 我们从PDF中提取页面
for pagenum, page in enumerate(extract_pages(pdf_path)):

    # 初始化从页面中提取文本所需的变量
    pageObj = pdfReaded.pages[pagenum]
    page_text = []
    line_format = []
    text_from_images = []
    text_from_tables = []
    page_content = []
    # 初始化检查表的数量
    table_num = 0
    first_element = True
    table_extraction_flag = False
    # 打开pdf文件
    pdf = pdfplumber.open(pdf_path)
    # 查找已检查的页面
    page_tables = pdf.pages[pagenum]
    # 找出本页上的表格数目
    tables = page_tables.find_tables()

    # 找到所有的元素
    page_elements = [(element.y1, element) for element in page._objs]
    # 对页面中出现的所有元素进行排序
    page_elements.sort(key=lambda a: a[0], reverse=True)

    # 查找组成页面的元素
    for i, component in enumerate(page_elements):
        # 提取PDF中元素顶部的位置
        pos = component[0]
        # 提取页面布局的元素
        element = component[1]

        # 检查该元素是否为文本元素
        if isinstance(element, LTTextContainer):
            print(i, 'LTTextContainer')
            # 检查文本是否出现在表中
            if table_extraction_flag == False:
                # 使用该函数提取每个文本元素的文本和格式
                (line_text, format_per_line) = text_extraction(element)
                # 将每行的文本追加到页文本
                page_text.append(line_text)
                # 附加每一行包含文本的格式
                line_format.append(format_per_line)
                page_content.append(line_text)
            else:
                # 省略表中出现的文本
                pass

        # 检查元素中的图像
        if isinstance(element, LTFigure):
            print(i, 'LTFigure')
            # 从PDF中裁剪图像
            crop_image(element, pageObj)
            # 将裁剪后的pdf转换为图像
            convert_to_images('cropped_image.pdf')
            # 从图像中提取文本
            image_text = image_to_text('PDF_image.png')
            text_from_images.append(image_text)
            page_content.append(image_text)
            # 在文本和格式列表中添加占位符
            page_text.append('image')
            line_format.append('image')

        # 检查表的元素
        if isinstance(element, LTRect):
            print(i, 'LTRect')
            # 如果第一个矩形元素
            if first_element == True and (table_num + 1) <= len(tables):
                # 找到表格的边界框
                lower_side = page.bbox[3] - tables[table_num].bbox[3]
                upper_side = element.y1
                # 从表中提取信息
                table = extract_table(pdf_path, pagenum, table_num)
                # 将表信息转换为结构化字符串格式
                table_string = table_converter(table)
                # 将表字符串追加到列表中
                text_from_tables.append(table_string)
                page_content.append(table_string)
                # 将标志设置为True以再次避免该内容
                table_extraction_flag = True
                # 让它成为另一个元素
                first_element = False
                # 在文本和格式列表中添加占位符
                page_text.append('table')
                line_format.append('table')

            # 检查我们是否已经从页面中提取了表
            # if element.y0 >= lower_side and element.y1 <= upper_side:
            #     pass
            # elif not isinstance(page_elements[i + 1][1], LTRect):
            if not isinstance(page_elements[i + 1][1], LTRect):
                table_extraction_flag = False
                first_element = True
                table_num += 1

    # 创建字典的键
    dctkey = 'Page_' + str(pagenum)
    # 将list的列表添加为页键的值
    text_per_page[dctkey] = [page_text, line_format, text_from_images, text_from_tables, page_content]

# 关闭pdf文件对象
pdfFileObj.close()

# 删除已创建的过程文件
os.remove('cropped_image.pdf')
os.remove('PDF_image.png')

# 显示页面内容
result = ''.join(text_per_page['Page_2'][3])    # text_per_page 看下数据格式
print(result)
View Code

 

doc docx

Python-dox:优点:能够解析docx格式文档;缺点:doc格式文档无法直接解析,需要进行转换为docx格式间接解析

# pip install python-docx
from docx import Document
doc = Document(file_path)

# 遍历文档中的所有段落
text = []
for paragraph in doc.paragraphs:
    text.append(paragraph.text)
print('*' * 200)

# 遍历文档中的所有表格
for table in doc.tables:
    for row in table.rows:
        for cell in row.cells:
            print(cell.text)
print('&' * 50)

# 遍历文档中的所有图片
for image in doc.inline_shapes:
    print(image.filename)
View Code

 

docx2pdf

from docx2pdf import convert
file = r'xx.docx'
convert(file, "output.pdf")

 

版面分析

文本分割模型在文档解析中的角色

单双栏区分

 

汇总 Marker、MinerU、olmOCR、Surya、GOT-OCR 2.0、Mistral OCR等十几个开源/闭源模型 

olmOCR

本地部署最强OCR大模型olmOCR!支持结构化精准提取复杂PDF文件内容!完美识别中英文文档、模糊扫描件与复杂表格!本地部署与实际测试全过程!医疗法律行业必备_哔哩哔哩_bilibili

olmOCR-7B:文档提取专用模型       GPU

Docling

看看ERC(5)- Docling 解析PDF之进阶篇

Docling:统一的多文档解析框架 支持多种文档类型的处理与转换          内置OCR性能有限,中文开源OCR模型需要自行支持

ParseStudio

想提取文字,要用 PyMuPDF;想识别表格,还得上 pdfplumber 或 Docling;结果提出来的数据格式还不统一,处理麻烦……

现在有了更优雅的选择:ParseStudio。专为PDF解析量身打造,它集成了Docling、PyMuPDF、LlamaParse三种解析引擎,API设计简洁,模块化架构让你随心切换解析器,轻松搞定多模态解析任务。

一站式PDF解析神器!统一封装Docling、PyMuPDF、LlamaParse,批量处理无压力!

 

Umi-OCR:开源、免费、离线、多功能的 OCR 图片文字识别软件 (qq.com) 

AI“落地”系列-RAG前置文档解析

MMDocIR:多模态文档检索新基准,提升长文档的智能检索能力

一个开源、高效、灵活的 PDF OCR 文字识别神器!

基于多模态大语言模型的 PDF 转 Markdown 工具MarkPDFDown

 


 

Pandoc:万能文档转换工具

256M参数多模态OCR神器,0.35秒解锁全文档奥秘!  CPU/低配 GPU

PDF文档解剖术!OCR神器+1,这个开源工具把复杂排版秒变结构化数据!      PDF Document Layout Analysis

MinerU

https://github.com/opendatalab/MinerU    

9.MinerU——RAG知识库预处理,PDF转Markdown文件(对数学公式友好)_哔哩哔哩_bilibili

PDF转markdown,AI知识库绝配,科研文献整理必备,MinerU将PDF转化为机器可读格式的工具_哔哩哔哩_bilibili

MinerU 是一个一站式开源高质量数据提取工具,支持 PDF、网页和多格式电子书的提取。 该项目解决了从各种文档中高效准确地提取数据的问题。

  • 支持多种文件格式,包括 PDF、网页和电子书。
  • 提供强大的表格识别功能,提升数据解析的准确性。
  • 多语言 OCR 支持,可检测和识别 84 种语言。
  • 优化内存使用,大幅降低硬件要求,提高性能表现。
  • 便于快速部署,并提供在线演示以展示功能。

 

MinerU 2.0以及远程调用

 

未开源

从复杂文档到AI秒懂的高质量数据:EasyDoc解析实战指南 

 

新出来的还没研究

PymuPDF4llm:PDF 提取的革命

利用LLM从非结构化PDF中提取结构化知识    PymuPDF4llm

PDF 文档提取和解析 API:使用最先进的 OCR 和 Ollama 支持的模型

 

 

>>>> 在线文档解析服务横评:TextIn、MinerU、MonkeyOCR 谁最适合企业知识库? (qq.com)

由于样本文档、参数配置的局限性,本次测试可能存在一定的随机性。 相对而言,TextIn 在线解析服务在表格识别、文档结构、跨页表格场景下比较优秀。

 


>>>> RAG:PDF解析难在哪,主流方案是什么 (qq.com)

目前常见的PDF 解析工具可以分为:

1. 轻量文本提取库:PyMuPDF、pdfplumber、pypdf, 原生文本提取路线,速度极快

2. AI 增强开源框架:Docling、MinerU、Marker-PDF , 集成布局分析模型,面向 RAG 场景

3. 收费 API:各大云厂商都有提供API ,托管服务,开箱即用

4. VLM 直接调用:GPT-4o、Gemini 2.5、Qwen3-VL ,最高理解能力,成本最高

 

VLM 将 PDF 页面光栅化为图像后直接输入多模态模型,由模型输出结构化文本。直接就绕过了复杂的坐标提取、字体解码、阅读顺序重建的所有中间步骤,对多栏、嵌套表格、合并单元格、手写内容的处理能力是应该是最强的了。

场景模型
综合最强(闭源) Gemini 2.5 Pro
综合最强(开源) Qwen2.5-VL-72B、InternVL3
文档 OCR Gemma 3、LLaMA 3.2 Vision、DeepSeek-OCR
轻量部署 Phi-4、Pixtral 12B、DeepSeek-VL2
中文场景 Qwen2.5-VL、GLM-4.5V

 

 

 

 

 

 

 

常见解析问题

RAG应用在生产环境中的PDF解析问题主要在解析层和内容层。

多栏混排导致阅读顺序错乱

问题描述

多栏布局是 RAG 解析中影响最大的单一问题。PDF 的物理存储顺序不保证阅读顺序,左栏和右栏的文字在内容流中可能交错排列。轻量解析库按存储顺序提取字符,不关心区域归属,导致左右栏文字被交织拼接,形成语义完全断裂的混合文本。

正确阅读顺序:
  [左栏] Introduction    [右栏] Abstract
  [左栏] We propose...   [右栏] Recent work...

PyMuPDF 默认提取结果(部分文档):
  Introduction
  Abstract              ← 从左栏跳到右栏第一行
  We propose...
  Recent work...        ← 又跳回左栏

LLM 处理这种交错文本时,看到的是两段完全无关内容的混合,无法推断语义关系,上下文理解质量大幅下降。

处理方案

按坐标排序是最基础的改善,对提取出的文字块按 y 坐标(从上到下)和 x 坐标(从左到右)重新排序,可以部分解决简单双栏问题,但对三栏以上、图文混排或非标准版式可能还是会出问题。

最好的解决方案是进行版面分析,比如先用布局检测模型(DocLayNet 或 PP-StructureV2 等)将页面划分为独立的文本区域,标记每个区域的类型和阅读顺序,然后按区域顺序拼接文字。

对于无法通过版面分析解决的极复杂版式(杂志排版、广告页面、非标准布局),那就得试试VLM了。

字体编码缺失导致乱码

问题描述

当 PDF 生成工具未嵌入完整的 ToUnicode CMap,或使用了自定义字体编码时,解析器无法将字形 ID 映射回 Unicode 字符,输出结果为乱码、方框(□□□□)或空白。比如:使用 Type 3 自定义字体、字体子集嵌入不完整、中文字体 Unicode 映射缺失、文档经过 PDF 优化压缩工具处理后编码表被裁剪。(可以用编辑器,比如cursor的,打开pdf,底部可以修改调整编码,然后就可以看到下面的内容~)

PDF 中显示:Attention Is All You Need
PyPDF2 提取:偛整匯數整數搔搔数搔數

处理方案

不同解析库对字体编码的处理能力存在差异,同一份出现乱码的 PDF,换用 PyMuPDF(MuPDF 引擎)可能正常提取,反之亦然。所以有时候可以先试试别的解析器。

如果多个解析器都无法正常提取,说明可能是字体映射信息在 PDF 层面确实缺失。此时绕过文本层、直接对页面图像做 OCR 是可行的替代方案,OCR 从像素识别字符,完全不依赖 PDF 内部的字体编码。

对于"视觉显示正常但文本提取乱码"的 PDF,最常见的就是字体编码缺失的情况,OCR 通常能比较好的解决。极少数情况下(例如 Type 3 字体 + 完全自定义字形形状),可能OCR 也无法正确识别,那就只能回到文档源头重新生成或寻找原始可编辑版本。

跨页内容断裂

问题描述

物理分页(基于纸张尺寸的固定分割)与逻辑分段(基于语义的可变分割)两者天然无法对齐。解析器按页面处理文档时,每个页面作为独立单元,跨越页面边界的内容被物理截断。

跨页类型出现频次可能导致的问题
段落跨页 常见 段落语义断裂,上下文不完整
句子跨页 一般 句子在页面边界处被截断,语义无法理解
表格跨页 一般 表头在一页,数据在下一页,行列关系丢失
图表与说明跨页 一般 图片和其对应说明文字被分离
列表跨页 一般 编号或项目符号列表被切断,后续条目失去上下文
代码块跨页 少见 代码缩进结构和上下文丢失

表格跨页影响比较大,比如表头行留在第1页末尾(有些复杂的可能注脚都占了很多内容~),数据行从第2页开始,解析器分别处理两页,输出的两段文字都缺乏完整语义。RAG 检索时若只命中其中一段,LLM 无法还原行列对应关系。

处理方案

段落和句子跨页的最常见的方案是分块阶段设置 overlap(相邻 chunk 之间保留若干 token 的重叠),可以确保跨页处的句子在某个 chunk 中是完整的。另外,就是语义分块(基于 embedding 检测语义边界后切割)效果更好。

表格跨页就不能依赖通用的 overlap 方法。可行方案是在解析阶段检测当前页是否以未结束的表格收尾(最后一个检测到的结构是表格行,但没有表格边框的结束信号),并将下一页开头的表格行与当前页合并后再处理。Docling 对这种场景有内置处理逻辑,MinerU 同样支持跨页表格连接。

版面分析也是解决跨页的比较好的方案。

页眉页脚污染索引

问题描述

页眉页脚在每一页重复出现,内容通常是文档标题、公司名称、页码、日期等。比如,一份100页的文档,相同或高度相似的页眉文字会出现 100次。这些内容一方面会产生大量近似重复的 chunk,另外一方面就是不同页的 chunk 因共享相同页眉而向量相似度偏高,影响检索的区分能力。

处理方案

位置过滤和跨页重复检测。

因为页眉页脚通常出现在页面顶部和底部固定区,所以过滤 y 坐标落在页面顶部 8%(这个具体数值具体调试着看看) 和底部 8% 区域内的文字块。这个方法对布局规整的文档效果稳定,对将页脚放在页面中下部的特殊排版可能过滤不足或过滤过度。

跨页重复检测就简单粗暴了。统计每行文字在全文档各页中出现的频率,将出现比例超过阈值(这个具体数值也得具体调试着看看)的行标记为重复内容并过滤。比如,可以遍历所有页面的文本,以行为单位统计跨页出现频次,频次 / 总页数超过阈值的行视为页眉或页脚候选,从所有 chunk 中移除。

图表信息丢失

问题描述

纯文本解析对嵌入 PDF 的图片、图表、流程图的处理结果是完全丢失的,在提取后不留任何内容,相关的引用文字在 RAG 中也就成了空引用。

处理方案

纯文本提取 + OCR 可以恢复图表中的文字内容(如表格、标注文字),但无法理解图表的视觉语义(趋势方向、比较关系、空间布局等等),这个时候唯一能解决就只有VLM。

先用文本解析构建索引并完成检索(一般来说文本内容足以定位相关页面),检索到相关页面后,将页面光栅化为图像再输入 VLM 进行深度理解,最后将文本内容与 VLM 输出结合生成回答。

参考文献与脚注处理

问题描述

参考文献和脚注在 PDF 中的物理位置与其被引用的位置相距较远,跟页脚还不一样的,解析器按页面顺序处理文档时,引用标记(比如,[1]、上标数字)所在的 chunk 和对应的参考文献/脚注内容所在的 chunk 会被分离到向量索引的不同位置。

脚注文字要么混入正文末尾,要么在页面分割时被截断丢失。比如,在法律和金融场景中,一般文档脚注常包含免责声明、例外条款丢失这些内容会导致 LLM 基于不完整信息生成误导性回答。

处理方案

版面分析模型能够区分正文区域和脚注区域,Docling 和 MinerU 的输出文档树中,脚注作为独立类型节点存在,可以选择将脚注内容附加到引用它的正文段落之后,或单独存储并在检索时通过元数据关联。

对于参考文献,完整的处理方案是在索引时解析引用链,将 [1] 对应的参考文献内容嵌入到引用它的 chunk 元数据中,检索时一并返回。这个方案在学术论文场景下效果最为明显,但实现复杂度高,需要先提取完整的参考文献列表并建立引用号到文献内容的映射。稍微简单点的方案是在检索后扩展上下文,也就是命中某个引用了 [1] 的 chunk 后,同时返回文档尾部参考文献章节的对应条目,由 LLM 综合两段内容生成回答。

标题层级在分块后丢失

问题描述

传统按 token 数固定分块的方式不感知文档层级结构,切割结果中每个 chunk 只包含该节的内容文字,不携带它在文档层级中的位置信息。

无向量 RAG 是否是下一代 RAG?

 文档树的优势了吧?

处理方案

最简单的方法是在分块时,为每个 chunk 构建其在文档层级中的完整路径,然后作为元数据附加在 chunk 内容前。

复杂一点的方法是不按 token 数切割,而是按文档的语义边界(章节、小节)切割,确保每个 chunk 对应一个完整的语义单元,不跨越标题边界,但这需要解析器输出保留层级结构信息。

好像也有人使用父子 chunk 策略,用小 chunk(128-256 token)做 embedding 和检索,命中后返回其父节点(完整小节,512-1024 token)给 LLM 生成回答。检索基于小 chunk 的语义精度,生成基于大 chunk 的上下文完整性,两个目标分别优化。

 

 

 

 

 

参考资料 还有很多方法,自行查看

 

 

 

参考资料:

https://mp.weixin.qq.com/s/9L_LwJvwn_F9C89J-yYezA  大模型下开源文档解析工具总结及技术思考  各种类型  代码很详尽 

https://zhuanlan.zhihu.com/p/344384506  Python操作PDF全总结|pdfplumber&PyPDF2

https://mp.weixin.qq.com/s/gCU1hYmmHpqiV9APHotrYA  只需2行代码,轻松将PDF转换成Word

十大PDF解析工具在不同文档类别中的比较研究 

https://mp.weixin.qq.com/s/W1TciuOp4FTBU09LHQYptQ  AI文档智能助理都是如何处理pdf的?