我把几百份散落 PDF 变成了能搜的资料库
手上攒了几百份资料:技术报告、标准、论文、手册,来源杂,格式以 PDF 为主。都是这些年陆陆续续下载的,命名规则换过好几次,同一个文件存了两三份的情况也有。
真正的问题不是”我找不到这个文件”,而是”我记不清哪个文件里讲过这件事“。检索只能靠文件名,而文件名往往跟内容对不上。
这篇文章记录我把它整理成可检索资料库的过程。不是教程,是我的实际步骤和踩过的坑。
第一步:先清点,别急着写代码
我最开始的冲动是直接写个脚本,全目录扫一遍开始抽取文本。后来忍住了,先做了清点。
清点做的事很简单:列出所有文件、大小、修改时间,算一遍内容哈希。就这三件事,立刻暴露了几个问题:
- 重复文件占比不低。同一个报告,一份是官网下载的,一份是从别处转存的,名字不同、字节几乎一样。光这一步就清掉了不少空间
- 命名混乱到什么程度。有的用报告编号,有的用标题,有的用下载日期。指望从文件名提取元数据是不现实的
- 文件类型比想象的多。除了 PDF,还有 DOCX、PPTX、扫描件,甚至有图片格式的”电子书”
这轮清点花的时间不多,但把后面所有工作的方向修正了:元数据只能从内容里提取,不能靠文件名。
第二步:文本抽取,最脏的一步
抽取本身不难,难在异常处理。
PDF 抽取大致分三类情况:
有文字层的 PDF,直接抽就行。工具上我用的是 pypdf 和 PyMuPDF 两个库——前者纯 Python、方便;后者对复杂版式和速度更友好。两边都留着,遇到抽取结果不对的时候互相验证。
扫描件,没有文字层,必须走 OCR。这一步的准确率跟扫描质量强相关,歪的、糊的、带水印的件,识别出来一堆错字。我的做法是:OCR 结果单独标记,检索时能命中,但引用前必须人工核对原文。
版式复杂的 PDF(双栏论文、带表格的报告),抽取出来的文字顺序会乱。双栏文献尤其明显——它会横着串行。这种情况我用一个笨办法:按页抽取并保留页号,检索到之后回去看原页。
这轮最大的教训是:抽取失败要报错,不要跳过。我第一版脚本对失败的 PDF 静默跳过,结果做了三百份以为做完了,实际漏了四十多份,还是后来查某个报告查不到才发现的。
第三步:切分与索引
切分粒度是个需要试的问题。
按整篇索引,检索精度太低——命中一篇三百页的报告,等于没定位。按固定字数切,会把一段完整论述切成两半。我最后采用的是折中:按章节标题切,没有明显标题结构的话退回到按页切。
切分之后每一段都带着一组来源信息:文件名、文件哈希、页号或章节、抽取方式(文字层还是 OCR)。这几个字段是后面追溯的依据,缺一个都不行。
索引方式我走过一条弯路。一开始想上全文检索引擎,配置、分词、中文处理都是活。折腾了两天之后我退回来,先用最土的办法:把每段文本落地成 Markdown,用 ripgrep 做关键词检索。
结果发现这个土办法覆盖了我九成的需求——我要找的是”哪份文献提过这个概念”,不是”给我按相关度排序的前十条”。等需求真的复杂到土办法撑不住了,再上检索引擎也不迟。
第四步:检索之外,还需要”相关”
资料库真正的价值出现在这一步:不只是”搜到”,而是”知道它是同一类”。
我的做法是给每份文献打几个维度:来源类型(官方报告、学术论文、行业材料)、年份、主题标签、以及我在笔记里对它的一句话评价。
有了这些维度之后,找资料的路径变成了两条:一条是从问题出发的关键词检索,另一条是从已知文献出发的相关推荐——比如”我读过一份讲装备维修体制的报告,还有哪些是同类的”。
几条经验
先手工跑通一遍,再自动化。 我一开始就想写个”全自动流水线”,结果卡在元数据提取上。后来改成先手工做十份,把流程摸清楚,再逐段自动化,反而快。
哈希去重省下的不止是空间。 重复文件会让检索结果出现一堆同内容不同名的命中,淹没真正有用的结果。
保留原始文件,别只留抽取结果。 抽取一定会有误差,原始 PDF 是最终的裁判。
给每份文献写一句话。 就一句话,写”它讲了什么、我为什么留它”。这句话在半年后比任何元数据都有用。
这套东西做下来,最大的收益不是检索变快了,而是我对自己手上有什么变得清楚了。
以前是”我记得好像有这么一份材料”,现在是”这份材料的第 47 页讲了这件事,来源是某某机构某某年”。这个差别在写东西的时候特别明显——以前要重新去找,现在直接引用。
至于索引引擎、向量检索这些更时髦的做法,等我先把中文分词的坑填了再说。