【锦图简历】程序对简历扫描件的识别流程

在做简历上传功能时,我低估了一个场景:扫描件 PDF——用户从 scanner 或微信保存的 PDF,肉眼看正常,文本层却是空的。此时 pdf-parse 一类库几乎抽不出字,用户却认为「我上传没问题」。

下面是我们在线上用的 分层提取 + OCR 回退 思路,供同类文档上传场景参考。
在这里插入图片描述

1. 先判断:有没有可提取的文本层?

流程第一步不是 OCR,而是 尽量便宜地拿文本

  1. 优先 Poppler pdftotext(结构化参数:行阈值、单元格分隔)
  2. 回退 pdf-parse 多种提取模式
  3. 统计「有效字符数」(去空白后),低于阈值(如 80 字)→ 判定为扫描件

这样能避免对可复制 PDF 做昂贵的 OCR。

2. 扫描件:渲染 → OCR → 规整

判定为扫描件后:

PDF Buffer
  → pdftoppm / pdf-to-img(按页渲染,DPI 建议 300 左右)
  → 图像预处理(灰度、对比度、锐化、小图放大)
  → Tesseract(chi_sim+eng,PSM 3/11/6 择优)
  → 文本后处理(去乱码行、拆粘连章节)
  → 简历规整(章节换行、列表符、经历行)
  → 规则诊断 / AI 分析

PSM 说明(简版):

  • 3:全自动分页,适合整页简历
  • 11:稀疏文本,适合 bullet 列表
  • 6:单块文本,部分模板反而更好

可对同一页跑多种 PSM,用 质量评分(有效字符比 + 章节词命中)选最优,而不是写死一种。

3. 工程踩坑(真实遇到过)
现象处理
Docker 缺语言包OCR 全乱码镜像预装 chi_simeng
多页 OCR 超时用户以为卡死限最大页数 + 流式进度
Nginx 60s 断连上传到一半失败proxy_read_timeout;OCR 阶段发 keepalive
双栏 Word 模板左栏技能与右栏经历串行宽图分列 OCR + 后处理去噪(另文详述)
DPI 过低小字号中文漏字150 → 300,小图再放大
4. 进度与体验

OCR 单页可能 20~40 秒,整份 90 秒不罕见。不要只给一个 spinner

我们采用 NDJSON 流式响应,步骤例如:extract → ocr → normalize → done,每步推送进度百分比与人话文案(「正在识别扫描版文字…」)。OCR 阶段长时间无业务输出时,额外发 心跳行,避免代理认为连接空闲而断开。

5. 代码结构(示意)

不必照搬,关键是 阶段可观测

// 伪代码:上传解析入口
async function parseResumeDocument(buffer, fileName) {
  onStep("extract");
  const textLayer = await tryExtractText(buffer);
  if (hasEnoughText(textLayer)) {
    onStep("normalize");
    return format(textLayer);
  }

  onStep("ocr");
  const ocrText = await ocrPdfPages(buffer); // poppler render + tesseract
  onStep("normalize");
  return format(cleanupOcr(ocrText));
}
6. 结论
  • 扫描 PDF 在中文求职场景里 不是边缘 case,是常态之一
  • 后端要能 自动回退 OCR,并在 UI 上 让用户等得明白
  • OCR 之后还要 规整 + 人工校对,尤其双栏模板

我们在产品 锦图简历 里按上述链路实现简历上传(Word / 可复制 PDF / 扫描 PDF / 图片)。若你也在做文档类 ToC 工具,欢迎评论区交流 Poppler 与 Tesseract 在容器里的打包方式。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值