简介
在文档处理时,尤其是大型复杂结构的文档时,按照字数进行分割,总会造成文本段的割裂,导致召回准确率降低
如果能精确的找到文档大纲和目录,从而按照文档的目录的大纲进行处理,则会提升更多的召回准确度
目前的方案使用 OCR 识别来获取标题,参考知乎文章
ChatPDF | LLM文档对话 | pdf解析关键问题
我发现如果 pdf 如果本身有大纲的话,也可以利用起来,毕竟 OCR 识别速度较慢,这样可以先解决一部分的问题
效果展示
如果 pdf 包含大纲,则可以使用 PyPDF2
进行提取
实现结果:
其中你可以获取一级、二级等多级标题,标题所在起始页和结束页位置,以及标题所在行距离当页顶部的距离,可以精确定位
2012年度报告 page = (0, 1), top = 534
第一节 重要提示、目录和释义 page = (1, 5), top = 770
第二节 公司基本情况简介 page = (5, 7), top = 770
|---- 一、公司信息 page = (5, 5), top = 701
|---- 二、联系人和联系方式 page = (5, 5), top = 368
|---- 三、信息披露及备置地点 page = (5, 6), top = 186
|---- 四、公司历史沿革 page = (6, 6), top = 755
第三节 会计数据和财务指标摘要 page = (7, 15), top = 770
|---- 一、主要会计数据和财务指标 page = (7, 9), top = 701
|---- 二、境内外会计准则下会计数据差异 page = (9, 10), top = 237
|---- |---- 1、同时按照国际会计准则与按中国会计准则披露的财务报告中净利润和净资产差异情况 page = (9, 10), top = 194
|---- |---- 2、同时按照境外会计准则与按中国会计准则披露的财务报告中净利润和净资产差异情况 page = (10, 10), top = 709
|---- |---- 3、境内外会计准则下会计数据差异说明 page = (10, 10), top = 505
|---- 三、报告期内非经常性损益的项目及金额 page = (10, 12), top = 426
|---- 四、重大风险提示 page = (12, 12), top = 714
第四节 董事会报告 page = (15, 38), top = 770
|---- 一、管理层讨论与分析 page = (15, 16), top = 701
|---- 二、报告期内主要经营情况 page = (16, 31), top = 366
|---- |---- 1、主营业务分析 page = (16, 21), top = 326
|---- |---- |---- (1)收入 page = (16, 18), top = 295
|---- |---- |---- (2)成本 page = (18, 19), top = 342
|---- |---- |---- (3)费用 page = (19, 19), top = 757
|---- |---- |---- (4)研发投入 page = (19, 19), top = 538
|---- |---- |---- (5)现金流 page = (19, 20), top = 250
|---- |---- |---- (6)公司主要供应商、客户情况 page = (20, 20), top = 465
实现代码
from PyPDF2 import PdfReader
from PyPDF2.generic import Destination
def process_outlines(outlines, prefix):
for i, outline in enumerate(outlines):
if isinstance(outline, Destination):
start_page = pdf.get_destination_page_number(outline)
# 跳过中间的子章节,直接获取下一个相同层级的章节
while i + 1 < len(outlines) and isinstance(outlines[i + 1], list):
i = i + 1
if i >= len(outlines) - 1:
# 最后一个节点,无法通过跳过中间子章节的方式获取,所以遍历所有子节点,找到最后一个子节点
last_page = outlines[-1]
while isinstance(last_page, list):
last_page = last_page[-1]
end_page = pdf.get_destination_page_number(last_page)
else:
end_page = pdf.get_destination_page_number(outlines[i + 1])
# 打印起始页和结束页位置,以及标题所在行距离当页顶部的距离,可以精确定位,
print(f' {prefix} {outline.title} page = ({start_page}, {end_page}), top = {outline.top}')
elif isinstance(outline, list):
process_outlines(outline, '|---- ' + prefix)
else:
print(f"Invalid outlines format. outline type is {type(outline)}")
if __name__ == '__main__':
pdf_path = '../xxxxxxx.pdf'
pdf = PdfReader(pdf_path)
outlines = pdf.outline
print(outlines)
# 遍历大纲
process_outlines(outlines, '')
结语
可以通过获取目录的方式后对 pdf 进行重新分割,提取文本段
并且在每一段文本上添加上具体的标题路径,可以提高召回准确率
后续还会继续补充如何使用大纲进行 pdf 解析,以及使用 OCR 寻找标题,并准确分割 pdf 文件