☰
PDF转TXT实战:六套方案、分诊选型与质量校验
2026/10/28 2:40:14 网站建设 项目流程

先说个真实感受:PDF 转 TXT 这件事,难的不是“找到一个工具”,而是“找到一个对的文件出来的结果能看”。我这些年整理资料库、做文本挖掘、给同事批量处理文档,手上过的 PDF 少说也有几万个,同一个文件换不同工具转,结果能差出十万八千里——有的满屏乱码,有的段落被拆得七零八落,有的干脆给你一张白纸,还有的把页眉页脚和正文搅在一起,读起来像天书。所以这篇不讲虚的,直接把我实际在用的六套方案摊开:办公套件直接导出、在线转换站点、桌面 PDF 编辑器的批量导出、命令行工具、Python 脚本流水线、扫描件 OCR 流水线,每一种都会说清楚它适合什么文件、关键参数怎么调、哪里最容易翻车。不管你是偶尔转一两份合同的学生,还是每天要处理几百份报表的运营,看完都能直接挑一条路走。前置提醒一句:PDF 转 TXT 的成败,八成在你打开文件之前就决定了,先判断文件类型,再谈工具。

1. 动手前先分诊:你的 PDF 到底是哪一类

1.1 有文本层和纯图像,完全是两码事

很多人以为 PDF 就是一种“文档格式”,其实它更像一个“打印结果的容器”。同一个 PDF,可能里面规规矩矩存着每个字符的编码、字体、坐标,也可能只是一张张拍好的照片贴上去。前者叫有文本层,工具读的是字符;后者叫扫描件或图片型 PDF,工具看到的只有像素点,你让它转文字,它只能干瞪眼,或者吐出一堆空白。

这个区别决定了后面所有方案的分岔路。有文本层的文件,任何一款转换工具基本都能出活,差别只在于排版保留得好不好、断行处理得干不干净。纯图像的文件,必须先过 OCR(光学字符识别)这一关,把像素认成字,才谈得上转 TXT。我见过太多人拿着扫描件去试七八个在线转换站,全失败了,然后骂工具垃圾——其实工具没毛病,是路走错了。所以第一步永远是分诊,不是选工具。

还有一种中间态,叫“伪文本层”:PDF 里确实有文本对象,但字体没有嵌入,或者字符映射表(ToUnicode)缺失,工具读出来的是一串谁也看不懂的符号。这种文件用普通方式转,出来的就是乱码,看起来像加密,其实只是缺了字典。判断方法很简单,后面会讲。

1.2 三分钟自检法:要不要上 OCR

不用装任何软件,三步就能判断。

第一步,用鼠标在 PDF 正文上拖一下。能选中、能高亮出蓝色底色,说明有文本层;怎么拖都是整块或者干脆选不中,基本就是图片。

第二步,复制一小段粘贴到记事本里。粘出来是正常文字,走文本层路线;粘出来是空白或者乱码,那就得看第三步。

第三步,把页面放大到 400% 以上看边缘。文字边缘有锯齿、有噪点、有轻微的倾斜或者纸张纹理,那是扫描件;放大后依然锐利干净,那是电子排版,即便复制出来乱码,也大概率只是字体编码问题,可以尝试换工具或换参数救回来,不一定非得上 OCR。

这三步做完,你心里就有数了。我一般还会顺手看一眼文件体积:纯文字的 PDF 通常几百 KB 到两三 MB,扫描件因为存的是图片,动辄十几 MB 甚至上百 MB,页数一样的情况下体积差十倍以上,基本可以确定是图像型。

注意:有些 PDF 是“混合型”,前几页是电子排版,后面附了扫描的签字页或附件。这种文件不要一刀切,用脚本按页判断、分而治之,比整本丢进 OCR 效果好得多,也快得多。

1.3 六种方案横向对比与选型建议

先把全局摊开,后面再逐个拆。下面这张表是我自己选型时用的判断依据,你可以直接对照。

方案适合的文件上手难度批量能力排版保留主要短板
办公套件导出少量、结构简单的电子版极低弱一般复杂版式容易乱序
在线转换站点临时几页、不敏感内容极低弱中等文件外传、页数限制
桌面 PDF 编辑器中等批量、需保留结构低强较好需付费、体积大
命令行工具大批量、服务器环境中极强好参数需记忆
Python 脚本定制化、需清洗后处理中高极强可控要写代码
OCR 流水线扫描件、图片型 PDF高强依赖预处理耗时、需调参

选型逻辑其实很简单:文件少、要求低,走前两条;文件多、要求稳,走中间两条;文件脏、要定制,走后两条;文件是扫描件,只有最后一条路。

我个人的默认路径是这样的:先拿命令行工具跑一遍,看看出来多少字、有没有乱码;如果字数明显偏少,说明文本层有问题或者根本是扫描件,再切到 OCR 流水线;如果字数正常但排版乱,再考虑用脚本做后处理清洗。这个“先试探、再决定”的顺序,比一上来就上重型方案省时间得多。

2. 方案一:办公套件直接另存,最省事的路径

2.1 打开方式决定了能不能成功

大多数人的第一反应是用办公软件打开 PDF,然后另存为文本。这条路能走通,但有个前提:你得让软件真的“解析”这个 PDF,而不是把它当图片预览。常见的误区是直接把 PDF 文件拖进窗口,有些套件会默认用“阅读模式”打开,这时候你看到的虽然清楚,但导出功能是灰的。

正确做法是通过“打开文件”菜单,明确选择 PDF 类型,让软件进入编辑或转换流程。第一次打开时通常会弹出一个转换提示,问你要不要把它转成可编辑文档,这一步别跳过,跳过之后导出的可能还是原样。转换过程本质上是软件在后台做一次版式识别,把文字块、段落、表格重新组织成它自己的文档模型,然后再由你另存。

我实测下来,这类方案对“单栏、纯文字、字号统一”的文件成功率很高,几乎不用二次修改。但只要文件里有多栏排版、跨页表格、复杂的文本框叠加,导出的顺序就容易乱:本来左右两栏的内容,会被按行拼在一起,读起来像两句话被腰斩后强行缝合。遇到这种,别硬扛,换方案。

还有一个细节值得注意:转换时的语言设置。如果文件里有大量专业符号或者非中文内容,把识别语言设成对应的语种,成功率会明显提升。默认设置有时候会把特殊字符认错,尤其是上下标、希腊字母、数学符号,转出来变形的概率不低。

2.2 导出纯文本时的参数与坑

转成可编辑文档之后,另存为纯文本这一步也有讲究。多数办公套件在“另存为”里提供纯文本选项,点进去通常还能选编码。这里请务必选 UTF-8,别用系统默认。我踩过这个坑:一次批量处理两百多份资料,全部用了默认编码,结果在有特殊符号的页面里,一部分字符变成了问号,而且是在保存那一刻就丢了,源文件没问题,纯粹是编码不兼容。

编码选错的表现很有辨识度:中文正常但引号、破折号、省略号变成乱码,或者生僻字变方块。UTF-8 能覆盖绝大多数情况,如果目标系统明确要求别的编码,再单独处理。

另外两个小开关值得留意。一个是“保留换行符”,勾上之后段落内的软换行会保留下来,好处是排版信息还在,坏处是每行末尾都会断开,后续要合并;不勾的话,有些软件会把整段压成一行,读起来反而不方便。我的建议是保留换行,后续用脚本统一清洗,可控性更高。另一个是“插入分页符”,如果只是取文字,可以关掉,省得后面还要清理一堆换页标记。

提示:导出之后先别急着批量处理,抽三页跟原文对照读一遍。重点看三处——段落开头有没有被吞字、数字和单位之间有没有多出空格、表格内容有没有串行。这三处没问题,剩下的基本都是小毛病。

3. 方案二:在线转换站点,快但要知道边界

3.1 什么场景适合用,什么场景绝对别用

在线站点的优势只有一个字:快。打开浏览器,拖进去,选输出格式,下载,全程不超过一分钟,不用装任何东西,手机也能操作。对于临时要看一份公开资料、转两页会议纪要这种需求,它确实是最优解。

但边界必须划清楚。第一,内容敏感的文件不要上传。合同、身份材料、内部报表、未公开的研究数据,一旦上传,文件就离开了你的控制范围,后续怎么存储、存多久、有没有被用于其他用途,你都无法掌握。这不是危言耸听,是基本的风险意识。第二,页数多的文件不要用。多数站点对免费用户有页数或体积限制,超了就排队、压缩或者直接拒绝,转出来的结果还可能是分段的。第三,格式复杂的文件不要指望它。在线站点为了兼容性,通常用比较通用的解析逻辑,遇到多栏、表格、公式,效果往往不如本地工具。

我的实际用法是:只把完全公开、无所谓的短文档丢进去,稍微重要一点的一律走本地方案。这条线划清楚,用起来就没有心理负担。

3.2 结果校验和常见翻车点

在线转换最容易被忽略的是校验。因为过程太快,人容易产生“已经好了”的错觉,实际上问题都藏在结果里。我一般会做三个快速检查。

看字数。如果一份十页的文档转出来只有几百字,那肯定是失败了,要么是扫描件,要么是解析中断,直接换方案。看开头和结尾。开头几行通常是标题和作者信息,结尾是参考文献或者页脚,这两处最能暴露问题:页眉页脚有没有被当正文混进去,最后一段有没有被截断。看特殊字符。搜索一下问号、方块、菱形符号,如果成片出现,说明编码或字体没处理好。

还有一种很隐蔽的失败:文字都在,但顺序全乱。表现是句子读起来颠三倒四,明明是“因此”开头的段落,跑到了上一段中间。这种是版面解析出错,本质上是工具把多栏内容按横坐标排序,结果把左右两栏交错拼接了。判断方法很简单,随便挑一段读一遍,通顺就没问题,读着别扭就是排序错了,换本地工具重新来。

注意:下载下来的文件名经常是一串随机字符,批量处理时先重命名成有意义的名称,否则过两天你自己都不知道哪个文件对应哪份资料。我习惯用“日期_来源_主题”的格式,简单但极其省事。

4. 方案三:桌面 PDF 编辑器的批量导出

4.1 批量任务怎么组织

当你手上有几十上百份文件,在线站点和办公套件就都不合适了,这时候桌面级 PDF 编辑器的价值体现出来。这类软件通常带“批量处理”或者“动作向导”功能,可以把“打开—导出为文本—保存到指定目录”这一串操作录制成一个任务,然后一次性喂给它几十个文件,去泡杯咖啡回来就完事了。

批量任务的关键在于目录规划。我一般的做法是建三个文件夹:源文件、输出文件、失败清单。源文件只读不改,输出文件按同名规则生成,失败单独记录。为什么要单独记失败?因为批量处理最怕的是“以为都成功了”,实际上有三五个文件静默失败,你过了两周才发现,还得回头重新找。让工具把失败的文件名写进一个日志,比事后一个个点开检查高效得多。

页面范围也是批量处理里容易忽略的设置。有些文件前几页是封面、目录、版权页,转成文本纯属噪音;有些文件最后附了几十页广告或者附录,也不需要。在批量任务里预设“从第 N 页到第 M 页”,能省掉大量后处理工作。当然,前提是这批文件的结构一致,如果结构参差不齐,还是全转再筛更稳妥。

4.2 输出设置里的几个决定性选项

桌面编辑器导出文本时,通常有几个选项,别一路点“下一步”跳过。

第一个是“保留版面布局”。勾上之后,工具会尽量用空格还原文字在页面上的横向位置,好处是表格和缩进能看出来,坏处是每行前面多出一堆空格,正文段落被撑得很宽。我的经验是:如果后续要给人读,勾上;如果后续要给程序做文本分析,别勾,纯文本更干净。

第二个是“识别文本 vs 提取文本”。有的软件提供“OCR 识别”选项,即使文件有文本层也会走识别流程,这会慢很多而且可能引入错误。有文本层就老老实实提取,别多此一举。

第三个是“字符编码和换行符”。编码选 UTF-8,换行符按目标系统选,Windows 环境用 CRLF,跨平台处理用 LF。这个细节在单文件上看不出差别,但批量处理上千个文件后,混用的换行符会让后续脚本出现莫名其妙的解析错误,比如某一行的内容被多切了一个空字符串。

这套方案我最常用于“结构相对规整、数量中等、需要保留一定版式信息”的场景,比如一批格式统一的报告或者教材。它的稳定性比在线站点强很多,又不需要写代码,属于性价比很高的中间路线。

5. 方案四:命令行工具,批量转换的主力

5.1 安装与最小可用命令

命令行工具是我日常用得最多的一条路,原因很实际:快、稳、可脚本化、不占资源。以最经典的 PDF 文本提取工具为例,它在多数 Linux 发行版的软件源里直接可装,Windows 下也能通过包管理器或者预编译包获得。

装好之后,最基础的用法只有一行:

pdftotext input.pdf output.txt

这行命令的含义是:读入 input.pdf,把能提取到的文字写进 output.txt。就这么简单,不需要打开界面,不需要等加载。我经常在终端里一口气跑几十个文件,几秒钟结束。

但默认参数出来的结果往往不够好。默认模式会尽量按阅读顺序输出,遇到分栏、表格就容易乱。所以真正干活时,我基本都会带上参数。最常用的一组是这样的:

pdftotext -layout -enc UTF-8 -nopgbrk input.pdf output.txt

三个参数各有分工,下面拆开说。

5.2 核心参数详解与组合套路

-layout是保留版面布局。开启后,工具会根据字符在页面上的坐标,用空格把它们摆回大致位置。效果是表格的行列关系、段落的缩进层次都能看出来,视觉上接近原文。代价是每行前面可能有一长串空格,正文被拉得很宽。我的用法是:需要人读的场合开,需要机器处理的场合关。

-enc UTF-8是明确指定输出编码。不指定的话,工具可能按本地语言环境走,在跨机器、跨系统传输时就容易出乱码。这个参数属于“写上不亏”的类型,养成习惯就好。

-nopgbrk是不要在页与页之间插入分页符。默认情况下,工具会在每页结束处插一个换页字符,本意是保留分页信息,但对纯文本处理来说就是噪音——你按行处理时,会莫名其妙多出一堆空行或者控制字符。除非你确实需要知道每页的边界,否则关掉更省心。

还有几个参数在特定场景下非常有用。-f 和 -l控制起止页,比如-f 3 -l 10就是只转第 3 到第 10 页,处理大部头的时候能省不少时间。-raw是按内容流原始顺序输出,不走版面重排,遇到那些坐标乱七八糟、重排后反而更乱的文件,这个模式有时候能救命。-bbox会额外输出每个词的坐标信息,做深度版面分析时才用得上,日常转换不必碰。

组合起来,我常用的两套是这样的:

# 给人看的版本,保留版式 pdftotext -layout -enc UTF-8 -nopgbrk report.pdf report.txt # 给程序处理的版本,纯文本流 pdftotext -raw -enc UTF-8 -nopgbrk report.pdf report_raw.txt

同一份文件,两套都跑一遍,对比一下哪个结果更通顺,用哪个。这招听起来笨,但比我凭经验猜参数快多了。

5.3 批量脚本与失败兜底

单个文件转换没什么技术含量,真正提效的是批量。下面这段脚本我用了很久,逻辑很简单:遍历目录下所有 PDF,逐个转换,记录成功和失败。

#!/usr/bin/env bash set -u SRC_DIR="./pdf" OUT_DIR="./txt" LOG="./convert.log" mkdir -p "$OUT_DIR" : > "$LOG" for f in "$SRC_DIR"/*.pdf; do [ -e "$f" ] || continue base=$(basename "$f" .pdf) out="$OUT_DIR/$base.txt" if pdftotext -layout -enc UTF-8 -nopgbrk "$f" "$out" 2>>"$LOG"; then size=$(wc -c < "$out") if [ "$size" -lt 200 ]; then echo "[WARN] 输出过小 ${size}B: $base" >> "$LOG" else echo "[OK] $base (${size}B)" >> "$LOG" fi else echo "[FAIL] $base" >> "$LOG" fi done

这段脚本里最值钱的不是转换那一行,而是那个“输出过小”的判断。命令行工具对扫描件是不会报错的,它只会安安静静地生成一个空文件或者只有几个字节的文件,然后返回成功。如果你不看日志,就会以为全都转好了。设一个字节数阈值,把可疑文件挑出来,后面再单独用 OCR 处理,这一步能省掉大量返工。

提示:文件名里有空格、括号、中文标点时,脚本里一定要给变量加双引号,否则路径会被截断。我最早写批量脚本时被这个坑了整整一个下午,几十个文件只转出来三个,还找不到原因。

6. 方案五:Python 脚本,把转换做成流水线

6.1 两个主流库怎么选

到了需要定制化的阶段,就该上脚本了。Python 生态里做 PDF 文本提取,常被拿来比较的是两个方向:一个偏重精细控制,能拿到每个字符的坐标、字号、字体;另一个偏重速度,底层是 C 实现,处理大文件非常快。前者适合需要做版面分析、表格还原、区域裁剪的场景,后者适合“我只要文字,越快越好”的批量任务。

我的选择逻辑是:文件数量在一百以内、需要精细处理,用前者;上千份、只取正文,用后者;两者都装,先跑快的,发现结果不满意再用慢的补刀。这不是模棱两可,是实际工作中的常态——你不可能一开始就知道哪份文件会出问题,先跑一遍看结果,比纠结选型高效。

安装很简单,用包管理器一行搞定:

pip install pdfplumber pymupdf

两个库的接口风格差别不小。精细控制那个库用上下文管理器打开,按页遍历,每页调提取方法;速度型那个库直接打开文档对象,按页号取文本。代码都很短,下面直接给完整版本。

6.2 一份可直接用的批处理脚本

下面这段是我实际在用的骨架,包含了打开、提取、清洗、写文件、异常兜底五个环节。

import os import re import sys import fitz # PyMuPDF SRC_DIR = "./pdf" OUT_DIR = "./txt" MIN_CHARS = 100 def clean_text(s: str) -> str: # 统一换行 s = s.replace("\r\n", "\n").replace("\r", "\n") # 合并被硬换行拆断的中文行 s = re.sub(r"(?<=[\u4e00-\u9fa5,。;:、)】])\n(?=[\u4e00-\u9fa5(【])", "", s) # 去掉纯页码行 s = re.sub(r"^\s*[-—]?\s*\d{1,4}\s*[-—]?\s*$", "", s, flags=re.M) # 压缩连续空行 s = re.sub(r"\n{3,}", "\n\n", s) return s.strip() def convert(path: str) -> tuple[str, str]: doc = fitz.open(path) parts = [] for page in doc: parts.append(page.get_text("text")) doc.close() return "\n".join(parts), "" os.makedirs(OUT_DIR, exist_ok=True) report = [] for name in os.listdir(SRC_DIR): if not name.lower().endswith(".pdf"): continue src = os.path.join(SRC_DIR, name) dst = os.path.join(OUT_DIR, os.path.splitext(name)[0] + ".txt") try: raw, _ = convert(src) text = clean_text(raw) with open(dst, "w", encoding="utf-8") as f: f.write(text) flag = "OK" if len(text) >= MIN_CHARS else "SUSPECT" report.append((name, len(text), flag)) except Exception as e: report.append((name, 0, f"ERROR:{type(e).__name__}")) for name, n, flag in report: print(f"{flag:8s} {n:>8d} {name}")

跑完之后看最后一列的输出,SUSPECT的进入 OCR 队列,ERROR的单独排查。这套流程的核心思想是“先分流、再精修”,不要指望一次转换就完美,把问题文件挑出来才是效率的关键。

6.3 参数调优:为什么你的结果和别人不一样

同样的库,不同人跑出来效果差很多,差别往往在这几个参数上。

第一个是行容差。精细提取库在判断“这两个字符是不是同一行”时,靠的是纵坐标差值,默认容差偏小,遇到稍微倾斜的扫描件或者行距不均的排版,同一行会被拆成两行,甚至每个字一行。把容差调大一点,比如从默认的 3 调到 5 到 8,合并效果会明显改善。反过来,如果原文行距很密,调太大会把两行粘成一行,所以要拿几页试。

第二个是字符间距容差。它决定“这两个字符之间隔多远才算插了空格”。中文排版里字距常常不均匀,容差太小会在一句话里塞进一堆空格,容差太大又会把本来该分开的词粘在一起。我的习惯是先设成默认值跑一页,数一数正文里多余的单个空格有多少,多就往上调一点。

第三个是排序方式。默认按坐标从上到下、从左到右排,这对单栏文档没问题,对双栏文档就是灾难。有些库提供“按块”提取,先把页面切成文本块,再按块的顺序输出,双栏场景下效果好得多。判断方法很简单:如果转出来的句子读着颠三倒四,但每句话本身是完整的,那就是排序问题,换块模式。

注意:脚本写好后别急着跑全量,先挑五种不同类型的文件各跑一遍——纯文字、双栏、带表格、带图注、扫描件。这五类过了,剩下的基本都是它们的变体,能省掉大半的返工。

7. 方案六:扫描件 OCR 流水线

7.1 渲染分辨率是成败的第一道关

扫描件没有文本层,只能靠识别。而识别的第一步不是识别本身,是把 PDF 页面渲染成图像。这一步的分辨率选错,后面怎么调参都是白费。

原理很直接:OCR 引擎认字靠的是笔画特征,笔画太细、像素太少,特征就丢了。分辨率太低,字糊成一团;分辨率太高,文件巨大、速度变慢,而且过度插值反而会产生伪影。经验值是 300 DPI,这是一个在清晰度和成本之间比较平衡的点。

具体算一下:A4 纸的尺寸是 210mm × 297mm,换算成英寸是 8.27 × 11.69。300 DPI 下,渲染出来的像素是 8.27 × 300 ≈ 2481,11.69 × 300 ≈ 3508,也就是大约 2480 × 3508 像素。这个尺寸下人眼看到的文字边缘是连续的,引擎也能抓到足够的笔画细节。如果原始扫描件本身只有 150 DPI 的信息量,你强行渲染到 300 DPI 也不会变清楚,只是把模糊放大了,这种情况反而应该保持原分辨率,靠预处理去噪。

用脚本渲染的写法大概是这样:

import fitz doc = fitz.open("scan.pdf") for i, page in enumerate(doc): pix = page.get_pixmap(dpi=300, colorspace=fitz.csGRAY) pix.save(f"page_{i:04d}.png")

这里顺手设成灰度,是有意为之——OCR 只关心明暗对比,不关心颜色,转灰度能砍掉三分之二的数据量,速度明显提升,准确率基本不受影响。

7.2 预处理四步:不做这一步准确率差一半

渲染出来的图直接丢给引擎,准确率通常只能到七八成。加上预处理,同样的引擎能提到九成以上。四步,按顺序来。

第一步,灰度化。如果是彩色扫描,先转成单通道。彩色信息对认字没用,反而会让二值化的时候出现色块干扰。

第二步,二值化。把灰度图变成黑白两色。这里别用固定阈值,用自适应阈值,按局部区域算阈值,这样即使页面一边亮一边暗(扫描仪打光不均很常见),也能保住细节。固定阈值的典型失败是:页面左上角清晰,右下角全黑或者全白。

第三步,去噪。扫描件上常有细小的黑点、纸张纤维、装订孔。用中值滤波能去掉大部分孤立噪点,同时保住笔画边缘。注意核大小别开太大,3×3 通常够用,开大了字会变粗、笔画粘连。

第四步,纠偏。扫描的时候纸稍微歪一点,人眼看不出,但引擎按水平方向切行的时候就会错位,表现为一行字被切成两半,或者两行被粘在一起。检测倾斜角的方法很简单:找出所有文字块的边界,算最小外接矩形的旋转角,然后反向旋转。一般倾斜在两度以内,纠偏之后识别率提升非常明显。

这四步做完,图就“干净”了。我见过很多人跳过预处理直接调引擎参数,调半天没效果,其实是输入的图本身就有问题——脏的输入不会因为参数调得好就变干净。

7.3 引擎参数:语言包和版面模式

预处理搞定,接下来是调用引擎。最容易忽略的是语言包。默认安装往往只带英文,识别中文会输出一堆莫名其妙的拉丁字母。装中文简体语言包,识别时用-l chi_sim,中英混排的文档用-l chi_sim+eng,让引擎同时加载两套模型,混排段落才不会一半好一半坏。

版面模式(PSM)的参数更关键,它告诉引擎“这一页大概长什么样”。常用的几个值:

模式值含义适用场景
3全自动分页分析默认,适合大多数规整文档
4假设为单列可变尺寸文本单栏正文、无表格
6假设为统一文本块版面干净、整页连续文字
11稀疏文本票据、表单、零散字段
12稀疏文本加方向检测旋转过的页面

我的习惯是先用 3 跑一页看效果,如果出现整段丢失或者顺序错乱,降到 6 或者 4 再试。表单类的文件直接上 11,不要用默认值,默认模式会试图把整页当成文章来分析,结果把字段全串起来。

一条完整的命令长这样:

tesseract page_0001.png out_0001 -l chi_sim+eng --psm 6 -c preserve_interword_spaces=1

最后那个参数是保留词间空格,对中文影响不大,但中英混排的文档开了之后,英文单词之间不会粘成一片。

7.4 后处理纠错:把准确率再往上顶一截

OCR 出来的文本一定有错,问题是怎么低成本地修。我的做法是建一个替换规则表,把高频错误固化下来,每次跑完自动过一遍。

高频错误基本集中在几类:形近字混认(比如“己”和“已”、“未”和“末”)、数字和字母混认(“0”和“O”、“1”和“l”)、标点符号漂移(中文逗号变成英文逗号、句号变成点)。前两类需要结合上下文判断,不好自动修;第三类最好办,直接做正则替换。

import re def fix_ocr(s: str) -> str: s = s.replace(",", ",").replace("。", "。") s = re.sub(r"(?<=[\u4e00-\u9fa5])\s+(?=[\u4e00-\u9fa5])", "", s) s = re.sub(r"[ \t]{2,}", " ", s) return s

第二行是去掉中文字符之间被误插的空格,这一条在处理 OCR 结果时特别有用,因为引擎经常在汉字的笔画间隙里“看到”空格。

提示:OCR 出来的结果,永远不要当成最终稿直接使用。哪怕准确率看着很高,关键的数字、日期、名称也必须人工抽查。我吃过亏——一份统计表转出来,准确率目测九成五,结果偏偏把“3.6”认成了“8.6”,一个数字毁了整张表。

8. 转换后的清洗与质量校验

8.1 断行、页码、页眉页脚怎么批量处理

不管用哪种方案,转出来的原始文本都是“半成品”。最常见的问题是硬换行:原文一个段落,转出来变成七八行,每行末尾断开。中文没有词间空格,断行处直接拼起来就行;英文要注意行尾的连字符,如果一行以-结尾,下一行接上时要把连字符去掉。

页码和页眉页脚是另一大噪音源。页码通常是独立成行的纯数字,或者带横线的数字,用正则就能清掉。页眉页脚麻烦一点,因为它们往往每页重复出现,我的处理办法是:统计所有行,找出出现在超过 70% 页面同一位置的行,判定为页眉页脚,批量删除。这个方法很粗暴但有效,唯一要小心的是某些书的章节标题也在页眉重复,删之前先抽样看一眼。

还有一个隐蔽的坑是零宽字符和软连字符。有些 PDF 在排版时插入了不可见的断字标记,复制出来看着正常,实际上字符串里多了一个看不见的字符,导致你后续做关键词匹配永远匹配不上。处理方法是在清洗时统一过滤掉 Unicode 的格式控制字符,一行正则的事,但不知道的人能查一整天。

8.2 三分钟校验法:怎么判断转得好不好

校验不需要逐字比对,三个指标够用。

字符数密度。算一下总字符数除以总页数,正常的中文文档每页在 800 到 1500 字之间,英文在 2000 到 3500 字符之间。如果某份文件算出来只有个位数,那肯定是失败了。这个指标能一把筛出所有“静默失败”的文件。

替换字符比例。统计文本里 U+FFFD(那个菱形问号)的数量,占比超过千分之一就说明编码或字体有问题。这个字符是解码失败的标志,出现就一定有环节没处理好。

中文占比。一份中文文档,如果统计出来汉字占比不到一半,说明大量内容变成了乱码或者符号,需要回退重转。

这三个指标写进脚本,跑完自动生成一张表,标红异常项。

校验指标正常区间异常含义处理方向
每页字符数中文 800-1500过低说明提取失败检查是否扫描件
替换字符占比小于 0.1%编码或字体缺失换编码、换工具
汉字占比大于 60%大量乱码排查文本层
连续空行数小于 3版面解析异常调整布局参数

9. 常见问题与排查速查表

9.1 乱码类问题的排查顺序

乱码是最常见也最让人抓狂的问题。排查要按顺序来,别乱试。

先确认文件是不是扫描件。前面说过的三步自检走一遍,如果是图片,乱码是必然的,直接上 OCR,别在文本层方案上浪费时间。

如果有文本层但复制出来是乱码,看乱码的形态。如果是一堆方框或者问号,那是字体没有嵌入,字符的视觉形状有,但编码映射丢了,这种情况任何文本提取工具都救不回来,只能 OCR。如果是一串看似有规律的拉丁字母或者奇怪符号,那是字符映射表缺失,可以试试换工具,某些工具在映射缺失时会走启发式还原,成功率不高但值得一试。

如果换了两三个工具都是乱码,别再试第四个了,直接 OCR。我见过有人为了省事试了十几个工具,花了两小时,其实 OCR 五分钟就搞定了。

9.2 排版类问题的排查方向

文字都在但顺序乱了,问题出在版面解析。先看是不是多栏:如果是双栏,把提取模式改成“按块”或者“按列”,很多工具提供这个选项。如果没提供,用带坐标输出的模式拿到每个字符的位置,自己按列切分——左边的归左栏,右边的归右栏,分别排序再拼接。

表格串行是另一个高频问题。表格在 PDF 里本质上是靠坐标对齐画出来的,没有真正的行列结构。工具提取时只能按位置猜,猜错就串。精细提取的库通常有表格识别功能,能返回二维数组,比自己写逻辑靠谱得多。如果表格结构复杂(合并单元格、跨页),识别效果会打折,这种情况我一般建议放弃自动提取,转成图片后人工核对,比反复调参省时间。

页眉页脚混进正文,前面说过用频率统计处理。还有一个变体是脚注:脚注文字常常在页面底部,按阅读顺序排会被插到正文中间,读起来莫名其妙。处理办法是按纵坐标切分页面,底部的区域单独提取,附加到页面末尾并加标记。

9.3 速查表:症状对原因对处理

症状最可能的原因处理动作
输出文件几乎为空扫描件无文本层走 OCR 流水线
满屏方框或问号字体未嵌入OCR 识别
拉丁字母乱串ToUnicode 映射缺失换工具试一次,不成则 OCR
句子顺序颠倒多栏排版按行排序切换块模式或按列切分
表格内容串行坐标对齐被误判用表格识别功能
段落被拆成单行行容差过小调大纵向容差
中文之间多空格字符间距容差过小调大横向容差
页眉页脚混入正文未做区域过滤按频率统计剔除
OCR 结果错字多分辨率低或未预处理提到 300 DPI 并二值化
批量处理后文件缺失路径含空格未加引号变量加双引号

10. 一些踩过坑之后的实操心得

先说一个被普遍低估的点:转换之前先做样本测试。不管你有多少文件,先拿三到五份代表性的跑一遍,确认参数、确认流程、确认输出格式,然后再上全量。我最早图快,直接几百份一起跑,结果参数不对,全部推倒重来,浪费的时间是测试的几十倍。这个习惯看起来慢,实际上是唯一快的做法。

第二个心得是保留中间产物。转换过程中生成的图片、临时文本、日志,别急着删。排查问题的时候,这些东西就是证据链。我曾经遇到一份文件转出来少了中间三页,查了半天以为是工具 bug,最后发现是原始 PDF 那三页本身是空白页嵌了图片,而图片里其实有字——如果当时把渲染的图片删了,我永远发现不了。

第三个是不要迷信准确率数字。OCR 报出来的置信度、工具宣传的识别率,都只能当参考。真正决定结果能不能用的,是你的具体需求:如果只是做全文检索,错几个字不影响;如果要做数据抽取,一个数字错了就是致命的。所以验收标准要按用途定,别拿一个通用指标套所有场景。

最后一个,关于工具选择:别追求一套方案打天下。我现在的实际工作流是三段式的——命令行先扫一遍分流,脚本做清洗和后处理,OCR 兜底处理脏文件。三套东西各管一段,加起来比任何一个“万能工具”都稳。你也不需要一步到位,先从一条路走通,遇到瓶颈再加下一段,这样学习成本最低,也不会因为工具太复杂而放弃。

真要说有什么一句话的经验,那就是:先看清楚文件,再选工具,最后一定要校验。顺序错了,工具再好也白搭。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询