把扫描版 PDF 转成 Excel
扫描版 PDF 转 Excel 的方法:把每一页图片识别成结构化字段,对照原图核对后,再导出一份 Excel 能干净打开的 UTF-8 BOM CSV。
扫描版 PDF 里并不是藏着一张表格,它本质上是一张文档的照片。每一页都是行、列和合计的图像,在人眼看来像一张表,但在计算机眼里只是一堆像素。这也是为什么扫描件几乎从来没有「导出到 Excel」按钮:根本没有单元格可导,只有一张图。想拿到真正的行,你必须先把页面重新识别成结构化字段,再把这些字段写成 Excel 能打开的文件。
本文讲的正是这套流程。你拿一张文档图片(扫描页、手机拍的照片、传真过来的小票),把上面的值抽取成有名字的字段,再导出一份能在 Excel 里直接打开的 CSV——UTF-8 带字节序标记(BOM),这样日文、韩文、中文都能落到正确的列里,而不会变成乱码。「扫描版 PDF 转 Excel」最终拿到的,就是这份 CSV。
为什么扫描件不能直接变成 Excel
扫描一张纸质发票,得到的是一张栅格图像——和 JPEG 照片是同一类文件。space-ocr 直接支持这些栅格格式:JPEG、PNG、GIF、BMP、TIFF 和 WebP。如果你的源文件是多页 PDF,有两条路:直接把 PDF 丢进 space-ocr 应用,它会自动把每一页渲染成图片;或者——如果你是直接调用 REST API——先把每一页导出成图片(PNG 或 TIFF)再上传。无论哪种方式,OCR 跑的都是页面图片。
引擎会读取每张图片,找出其中的值,并在能定位的范围内一并返回这些值来自页面哪个位置的来源坐标:一个 0–1000 归一化的轴对齐 box,以及跟随纸面倾斜的四点 quad。定位不到来源区域的值,或者字符和纸面印刷对不上的值,不会悄悄通过,而是被标记为待复核。一旦页面被结构化成字段,转成 Excel 不过是下载一份 CSV 而已。真正难、也真正值得做好的部分是「识别」,而不是「导出」。
从文档图片到结构化字段
上传一张文档图片——或者干脆丢进一张照片或一份 PDF——值就会以有名字的字段形式返回,而不是一大段文字。最快的办法是让应用替你推荐字段:把页面丢进去,它会自动给出一套字段结构,不用任何配置。如果你已经想好要哪些列,也可以自己定义列(字段结构):把列名和类型定下来,之后传进这张 sheet 的每一页都按同一套定义识别。来看一张扫描件怎么变成带标签的列:
对于有重复行的文档——发票的明细行、小票上的商品——可以声明一个 array 字段,并带上子列。页面上的每一行都会变成独立的一行,这正是表格需要做加总时你想要的样子。如果你专门要处理这类重复行,可以看从发票中提取明细行,里面有字段定义的细节。
{
"image": "https://example.com/scanned-page-01.png",
"imageType": "url",
"fields": [
{ "name": "vendor", "type": "string" },
{ "name": "invoice_date", "type": "string" },
{ "name": "total", "type": "string" },
{
"name": "line_items", "type": "array",
"children": [
{ "name": "description", "type": "string" },
{ "name": "unit_price", "type": "string" },
{ "name": "qty", "type": "string" }
]
}
]
}返回的值不会被我们归一化。 印在纸上的 7,855 返回的仍然是 7,855——逗号、小数点、全角字符都按读到的写法保留,不会被改写成某种标准数字,所以你的合计能和纸面对得上账。应用里看到的货币符号只是界面装饰,并不是值的一部分。在字段描述里怎么写都改变不了这一点——只有贴着纸面的写法,坐标和比对才有意义。不过 values 是模型读出来的文本,需要逐字严格比对时,请用 evidence.printed_text,也就是那个坐标处的原始 OCR 字符。如果你需要一个能参与计算的数字,API 会把它放在印刷值旁边一起给你:把字段的 type 声明为 number、integer 或 date,响应里就会带上与 values 结构一致的、已解析的 normalized。
先核对,再导出到 Excel
在把任何东西导入 Excel 之前,先把识别结果核对一遍。把鼠标移到某个值上,原图里对应的区域就会高亮,你的视线能直接落到那个位置,而不必把整张扫描件重读一遍。
你也不必逐个用肉眼扫过去,因为识别结果自带一份待办清单。data.review.flagged 里,每一个需要复核的值都带着它的 path 和被标记的 reasons:字符和纸面对不上是 text_mismatch,定位不到来源区域是 nobox,声明为必填的字段返回为空是 missing。单元格的 verified 就是这个判定,只要有任何一条理由被标记就是 false。处理这份清单即可,其余的不用动。evidence.match_ratio 是挂在单元格上的辅助证据,并不是让你拿去卡阈值的数字。
导出能在 Excel 里打开的 CSV
字段看起来没问题后,就导出这张表。你会得到一个 <sheetName>.csv,表头那一行就是你设的列名;array 字段会展开成 column.child 这样的列,重复的明细行会展开成多个子行。文件是 UTF-8 带 BOM 的,正是这个细节让 Excel 双击时能干净地打开 CJK(中日韩)文本。你做过的任何手动修正,都会在导出时覆盖原本的 OCR 值。
导出本身在 Free 和按量付费方案下消耗 1 个点数,Starter 和 Pro 方案下载免费。相同数据再次下载,在任何方案下都不计费。
在 Excel 里打开它很简单:直接双击那个 .csv。因为有 BOM,Excel 会自动把它当成 UTF-8 读取——不用文本导入向导,也不会出现乱码。接下来如果你需要原生工作簿,另存为 → .xlsx 即可。如果你的最终目标只是一条纯 CSV 的处理流水线、并不非得是 Excel,那么配套的扫描文档转 CSV指南把同一套导出从头到尾讲了一遍。
通过 API 批量处理
如果是一整个文件夹的扫描件,先用你的列结构创建一张 sheet,然后把页面图片上传到这张 sheet。每张图片都会按这套结构识别,并作为行追加进去,之后就能一次性导出成一份 CSV。
POST /upload 的上限是每次请求最多 20 个文件、单个文件 20MB、整个请求 28MB,超过会返回 413。费用是每页 1 个点数($0.05,含税)。调用默认是异步的:响应里带回一个 jobs[],结果通过 ocr.completed webhook 或轮询 GET /jobs/{jobId} 取回。像下面这样加上 wait=true 则改为同步等待,每张图片最多等 30 秒,没赶上的项目会以 status: "pending" 返回,稍后再取。需要重试时带上 Idempotency-Key,重复的请求会直接回放已缓存的响应,不会重复扫描。完整的请求/响应结构见 API 文档。
curl -X POST https://api.space-ocr.com/upload \
-H "Authorization: Bearer $SPACE_OCR_API_KEY" \
-F "path=/Invoices 2026" \
-F "files=@scan-page-01.png" \
-F "files=@scan-page-02.png" \
-F "wait=true"扫描版 PDF 转 Excel 怎么做
- 添加你的 PDF 或页面图片在 space-ocr 应用里,直接把 PDF 丢进去就行——每一页都会自动渲染成图片,根本不用转换。如果你是直接调用 REST API,那就先把每一页导出成栅格图像,因为引擎读取的是栅格图像(JPEG、PNG、GIF、BMP、TIFF、WebP),而不是 PDF 字节。
- 把页面识别成字段把上面的值抽取成有名字的字段。最快的办法是让应用根据页面自动推荐字段;如果你已经想好要哪些列,也可以自己定义列结构。对于重复的明细行,声明一个 array 字段。
- 核对各个值把鼠标移到字段上,高亮显示它在原始扫描件上的读取位置。只需要复核被标记的值——API 会把它们连同理由一起放在 data.review.flagged 里,其余的保持原样即可。
- 导出 CSV把这张表导出成 CSV。它是 UTF-8 带 BOM 的,会把 array 明细行展开成子行,你做过的任何手动修正都会覆盖原本的 OCR 值。
- 在 Excel 里打开双击这个 CSV——Excel 会读取 BOM,把你的行打开,列对得整整齐齐,CJK 文本也完好无损。需要原生工作簿就另存为 .xlsx。