把扫描版PDF转成Excel:表格导成CSV,告别乱码
扫描版PDF是图片,无法复制,贴到Excel还会乱码、明细被挤进一个单元格。把PDF拖进space-ocr,各页会自动转成图片,先定义好列,每页就成为一行,并导出为UTF-8 BOM的CSV——用Excel打开也不乱码,导入freee、MoneyForward、弥生也很顺。失败不计费。
打开一份扫描的发票或收据PDF,你第一时间就会发现 ── 没法复制。想选中文字却拖不动,连一个数字都拾不起来。原因很简单:扫描版PDF不是表格数据,而是「拍下了表格的一张图片」。在人眼里它是一行行、一列列、还有合计,可对计算机来说,那只是一堆像素。
于是很多人接下来的做法都一样:打开Excel,对着PDF一行一行手动录入;或者硬着头皮复制粘贴,结果呢 ── 中文/日文变成 譁�蟄怜喧 一样的乱码,明细整段挤进一个单元格,版式彻底乱掉。如果你是搜「扫描PDF转Excel」「PDF表格转CSV」「Excel乱码 解决」找到这篇文章的,那痛点八成就在这儿 ── 你想要的,无非是列对齐、能导进财务软件的数据而已。
结论:不用手动录入,也不用复制粘贴
你要做的,就是把PDF直接拖进 space-ocr 的应用。应用会自动把每一页转成图片,再把页面里的值读成带名字的字段。只要一次性定好列(往来单位、日期、合计、明细……),就能做到一页=一行,逐行堆进表格,最后导出成CSV。
关键就在这个CSV:它带 UTF-8 BOM,所以在Excel里双击打开也不会乱码。明细不会挤进一个单元格,而是规规矩矩地拆成各自的行和列。语言由引擎自动判别,无需你手动指定。
为什么扫描件没法「直接」进Excel
纸张被扫描的那一刻,文件内容就和照片一样,是位图(栅格图像)。里面没有嵌入文字信息,所以Excel的「获取数据」或复制粘贴都没法把它当表格读出来。因此要分两步:
- 读图并结构化(每一页 → 一行带名字的字段)
- 把这些行写成CSV(用Excel能顺利打开的格式)
在 space-ocr 的应用里,你把PDF丢进去,它会先把每一页自动转成图片(PNG)再做OCR。所以你这边不用自己把页面切成图片 ── 拖进PDF就行。难点在第①步「读取」,把这一步做准,正是根除乱码和明细挤压的捷径。
从原图 → 抽取表格:扫描件变成「列」
你拖进去的文档,值不再是一堵文字墙,而是带名字的列。列可以自己定义,也可以让引擎来提议(调用 API 时用 fields 声明,或传 autoFields: true 让它提议)。来看一张扫描件是怎么变成带标签的一行的。
对于有重复行的文档(发票的品目、收据的商品),可以在 array 类型的字段里声明子列。这样页面上的每一行都会变成各自独立的一行,在Excel里就能直接求和 ── 这正是「明细挤进一个单元格」问题的解法。明细怎么搭,读取发票、送货单并转成Excel 里讲得更细。
日期和金额这类列,还可以在 type 里声明标量类型(date / number / integer)。data.values 里保留的仍是票面上印着的原样写法(2026年3月31日、1,200,000),另外会多出一层 data.normalized,里面是确定性解析后的值 ── 日期为 ISO 格式,金额是能直接参与计算的数字。解析不了的叶子返回 null,原因写在 data.cells[路径].normalized.error,同时以 type_mismatch 进入 data.review.flagged。类型声明不会传给模型,所以它不会改变识别结果本身 ── 改变的是:在进 Excel 或财务软件之前,你另外拿到一份能直接计算的值。
{
"image": "https://example.com/scan-invoice-p01.png",
"imageType": "url",
"fields": [
{ "name": "vendor", "type": "string" },
{ "name": "invoice_date", "type": "date" },
{ "name": "total", "type": "number" },
{
"name": "line_items", "type": "array",
"children": [
{ "name": "description", "type": "string" },
{ "name": "unit_price", "type": "number" },
{ "name": "qty", "type": "integer" }
]
}
]
}坐标不是AI凭想象编出来的。 语言模型返回的只是值的文本,以及「用了哪些词元(token)」这类提示,并不返回框本身。引擎会先把这个值的文字,与视觉OCR在页面上实际检测到的符号逐字符比对(以字符级比对为主的路径),再把框放到真实的像素位置上。比对跑过的值会留下一个匹配率作为依据(evidence.match_ratio ── 这个值的文字有多少比例真的在页面上找到了)。如果模型返回了词元提示,有时会用它来覆盖部分字段的位置,但在重复行里提示可能张冠李戴,所以不会照单全收,而是通过列聚类和行一致性检查来验证和校正。要点不是「AI不会错」,而是比对得上的值会留下依据,比对不上的值本身就会浮到待核对清单里(定不下位置就是 nobox,文字对不上就是 text_mismatch)。坐标会同时返回 box(xmin / ymin / xmax / ymax)和跟随页面倾斜的四点 quad,两者都是 0〜1000 归一化(不是像素)。
导入前先抽查
在送进Excel或财务软件之前,你可以当场核对读取结果。把鼠标移到某个值上,原始扫描图里对应的位置会高亮,不用通读整份文档,视线一下就能落到那儿。
不必逐个复核。引擎会给自己没读顺的值打上待核对标记,从这些开始看就行。在应用里,被标记的单元格会着色,理由(text_mismatch、missing 等)显示在气泡里;通过 API 拿到的是同一份清单 data.review.flagged,每一项都带着路径和理由数组 ── 要核对的条数就是这份清单的长度。它不靠一个固定的分数阈值来卡。
导出Excel能打开的CSV
值都没问题后,把表格导出成CSV。表头一行排着列名,array 字段会展开成 列名.子列名,重复的明细拆进子行。文件带 UTF-8 BOM ── 正是这一点,让Excel只要双击就能正确打开中文、日文、韩文。你手动改过的单元格,会在导出时覆盖原来的OCR值并体现出来。
用Excel打开很简单 ── 双击那个 .csv 就行。有了BOM,Excel会自动识别成UTF-8,既不用走文本导入向导,也不会乱码。如果你需要原生工作簿,从这里另存为 → .xlsx 即可。
要直接拿去财务软件也一样,出口都是这个CSV。freee、Money Forward、弥生等,用各自的CSV导入功能读进去就行(不是官方API的自动对接,而是导入CSV文件)。只把收据导成CSV的流程,把收据转成CSV 里单独整理了。
批量处理:用API跑大量扫描件
想把一整个扫描件文件夹都处理掉时,先建一张带列结构的表格,再往里上传页面图片。每张图片都会按这套结构读取、作为行追加进去,最后能导成一份CSV。请求/响应的具体格式,见 API文档。
curl -X POST https://api.space-ocr.com/upload \
-H "Authorization: Bearer $SPACE_OCR_API_KEY" \
-F "path=/請求書 2026" \
-F "files=@scan-p01.png" \
-F "files=@scan-p02.png" \
-F "wait=true"价格很清楚。 每 1 点数 $0.05 按量计费,每月 100点数免费额度(无需信用卡),面向团队的固定套餐 Pro $39/月。而且 识别失败的那部分不计费 ── 没出结果就不收钱。导出CSV在免费额度和按量计费下需 1 点数(同一份数据再次下载免费),Starter 和 Pro 则下载免费。费用一目了然,可以先拿手头的扫描件试一试再决定。
把扫描版PDF转成Excel的步骤
- 把PDF拖进应用把扫描版PDF拖进 space-ocr 的应用。应用会先把每一页自动转成图片(PNG)再做OCR,所以你不用自己把页面切成图片。
- 定好列(结构)用自己的字段定义或自动提议,定好往来单位、日期、合计等列。把日期声明成 date、金额声明成 number 这样的标量类型,就会在 data.normalized 里多一份确定性解析后的值。像发票品目这种重复的明细,在 array 类型里声明子列,每一行就会变成各自独立的一行。
- 抽查各个值把鼠标悬到字段上,原扫描件的对应位置会高亮。引擎没读顺的值会带着待核对标记和理由(如 text_mismatch、missing)返回,先从这些看起,不必逐个复核。
- 导出CSV把表格导出成CSV。文件带 UTF-8 BOM,array 的明细会展开进子行,你手动改过的地方会覆盖原来的OCR值并体现出来。
- 用Excel或财务软件打开双击CSV,Excel会把BOM识别成UTF-8,中文/日文不乱码,以列对齐的行打开。需要原生格式就另存为 .xlsx。要导入 freee、Money Forward、弥生,则用各软件的CSV导入功能读进去。