结论:github.com/unidoc/unipdf/v3 是 go 中解析 pdf 文本最稳方案,但必须设置 uni_pdf_license_key 环境变量(免费试用30天),支持用户密码解密和较好中文提取;v3 与 v2 不兼容,需重写逻辑,且不处理纯扫描件。

用 github.com/unidoc/unipdf/v3 解析 PDF 文本最稳
直接说结论:Go 原生不支持 PDF 解析,必须依赖第三方库;unidoc/unipdf/v3 是目前最成熟、中文支持较好、API 清晰的选项(注意是 v3,不是已归档的 v2)。它底层基于 PDF 规范解析,能处理大多数带文字图层的 PDF(不含纯图片扫描件)。
常见错误现象:pdfcpu extract text 报 no text content found,或 gofpdf 类库根本没文本提取能力——它们定位不同,别混用。
- 安装命令必须加
UNI_PDF_LICENSE_KEY环境变量(免费试用 30 天,需注册获取):UNI_PDF_LICENSE_KEY=xxx go get github.com/unidoc/unipdf/v3/...
- 若跳过 license,运行时会 panic:
license not set: please set UNI_PDF_LICENSE_KEY - v3 的 API 和 v2 完全不兼容,旧项目升级要重写解析逻辑
提取文本前先判断是否含可选中文字符集
很多 PDF 中文乱码,不是解码问题,而是字体嵌入缺失或使用了非标准 CID 字体。unipdf 默认尝试用内置映射表还原 Unicode,但对某些国产 PDF 编辑器生成的文件仍可能失败。
实操建议:先用 pdfcpu info 查看字体信息(非 Go 代码,但排查必做):
pdfcpu info your.pdf
重点关注输出里的 Font Name 和 Encoding 字段。若出现 Adobe-GB1 或 UniGB-UTF16-H,基本能正常提取;若只有 Identity-H 且无 ToUnicode CMap,大概率需要手动指定字体路径或改用 OCR。
- 代码中可通过
pdfReader.PageCount()验证是否成功加载,避免空页导致nil pointer - 对每页调用
page.GetText()前,先用page.IsContentStreamValid()过滤掉损坏页 - 不要假设所有页面都有文字——扫描件、图表页、加密页都可能返回空字符串
处理密码保护 PDF 的正确姿势
unipdf 支持用户密码(user password)解密,但不支持所有者密码(owner password)强制限制下的内容提取(比如禁止复制)。如果 PDF 打开要输密码,大概率是用户密码,可解;如果只是“禁止复制”但能正常阅读,那可能是 owner password,unipdf 无法绕过。
关键点在于初始化 pdf.Reader 时传入密码:
reader, err := pdf.NewPDFReader(f, "your_password")
- 密码为空字符串
""表示尝试无密码打开,失败再试其他密码 - 若密码错误,
NewPDFReader返回err != nil,且错误信息含invalid password - 切勿硬编码密码到源码里;生产环境应从环境变量或 secret store 加载
- 加密 PDF 的字体和编码信息可能被混淆,即使解密成功,仍可能出现乱码——这是 PDF 标准限制,非库缺陷
性能瓶颈常卡在字体解析和 Unicode 映射
解析一页含 50+ 嵌入字体的 PDF,page.GetText() 可能耗时 200ms+。这不是 CPU 瓶颈,而是 unipdf 内部反复查找 ToUnicode CMap 和构建字符映射表所致。
优化方向很实际:
- 批量处理时,复用同一个
pdf.Reader实例,避免重复解析全局字体字典 - 对纯文字报告类 PDF,可跳过图像资源加载:设置
pdf.ReaderConfig{SkipImageLoading: true} - 不要在 HTTP handler 里直接调用
GetText()——超时风险高;建议预处理并缓存结果,或丢进异步队列 - 若只需关键词匹配,可用
page.GetContentStreams()+ 正则粗筛原始操作符流(如Tj,TJ),比完整文本提取快 3–5 倍,但需自己处理编码和换行
真正难的从来不是“怎么拿到文本”,而是“为什么拿不到”——字体缺失、权限锁死、扫描件伪装、流压缩异常……这些不会报错,只会静默返回空字符串。多打几行日志,比换库更管用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











