用收据 OCR 转成 CSV,再导入会计软件
收据小票手动录入、Excel 打开乱码、明细全挤进一个单元格、PDF 没法复制——把这些月末苦活替换成「拍照→定义好列→一张=一行→导出 CSV→导入 freee/マネーフォワード/弥生」的实操指南。明细用数组列展开成一件商品=一行,UTF-8 BOM 杜绝乱码,每个值都带 bbox+match_ratio 可与原件核对。每张 ¥10、失败不计费、每月 100 张免费额度。
月末。从钱包和抽屉里翻出来的一沓收据和小票。请想象一下把它们一张张手动录进会计软件的那种活:敲店名、敲日期、敲合计,再换下一张。攒到三十张,注意力早就散了,金额多打一位数都未必发现。「收据手动录入」之所以是苦差事,不在于量大,而在于它「明明很简单,却又错不得」这个性质。
想着扫描了贴进 Excel 吧,结果又撞上另一堵墙:打开 CSV,中文/日文全是乱码;小票上的明细(买了哪些商品的清单)一换行就被压进同一个单元格;PDF 形式发来的收据连复制都做不到。最后还是把原件摊在屏幕旁边,盯着一行行重新敲一遍——这样数字化到底图个啥,真说不清。
这篇文章,就是要把这套活替换成 「拍下收据 → 把列定义好一次 → 上传后一张=一行 → 导出成 CSV 导入 freee(freee 会计)/マネーフォワード(Money Forward)/弥生(弥生会计)」 的实操指南。它不是产品说明书,请当成「月末那一沓收据该怎么收拾」的经验来看。
先上手试试(无需上传,10 秒搞定)
下面这个示例,是把两张真实收据跑了一遍 OCR 的结果。把鼠标移到任意一个字段(店名、日期、合计、明细)上,它是从图片哪个位置读出这个值的,就会在原件上高亮出来。不用上传,也不用登录。先来确认一下「读出来的值,是不是真的指向原件上的那个位置」。

Every value carries a verified on-page location — bbox + 4-point vertices + match_ratio — on a 0–1000 normalized grid (0,0 top-left → 1000,1000 bottom-right), the same shape the live API returns. Hover a field to trace it back to the pixels it came from.
从收据 OCR 到 CSV,真实的 4 个步骤
要做的事其实不多。下面按顺序用视频一步步看。
- 上传原图 ── 把拍好或扫好的收据,灌进事先定好的列(店名、日期、合计、明细…)。
- 在抽取表里确认 ── 一张变成一行,列自动填满。
- 用 bbox 与原件核对 ── 可疑的值点一下单元格,原件上对应位置就会亮起,肉眼立刻核查。
- 导出 CSV ── 直接导出成能导进会计软件的 CSV。
先看步骤一:上传拍好/扫好的收据后,每张图片都会按你定义的列被结构化。
列(schema)只需定义一次
关键在于:一开始就把列定下来。 像「店名、分店名、日期、合计、明细」这样把想要的项目先定义好,之后上传的所有收据都会按同一套形态(同一 schema)整理成一行。哪怕各家店的排版千差万别,输出出来的行的形态都一样。也正因为如此,最后才能汇成一张 CSV。
「明细全挤进一个单元格」问题的真相
手工填 Excel 时最头疼的,就是小票的明细(买了哪些商品的清单)。一张收据上有十行商品,你要么把它塞进一个单元格,要么手动拆成十行。
在 space-ocr 里,明细被定义成数组(array)类型的列。导出 CSV 时,这个数组列会 展开成 明细.商品名 明细.单价 明细.数量 这样的子字段,并按一件商品=一行纵向铺开。也就是说,它不是「挤进一个单元格」,而是一开始就以会计、汇总都能直接处理的行的形态出来。
{
"columns": [
{ "name": "店舗名", "type": "string" },
{ "name": "支店名", "type": "string" },
{ "name": "日付", "type": "string" },
{ "name": "合計", "type": "string" },
{
"name": "明細",
"type": "array",
"children": [
{ "name": "商品名", "type": "string" },
{ "name": "単価", "type": "string" },
{ "name": "数量", "type": "string" }
]
}
]
}可疑的值,点一下就跳到原件去核对
读取完成后,你不必照单全收。点一下在意的单元格,原件图片上「就是从这里读出来的」那个位置就会高亮。视线一下子被拉过去,整沓收据抽查起来特别快。
它之所以管用,是因为每个值都连带返回 读取位置(bbox)和一致度(match_ratio)。一个值到底来自图片哪里,人和系统事后都能回溯——也就是说,可审计。
这些坐标,不是 AI 答出来的「应该在这儿吧」的数字。 语言模型返回的,只有值的文本,以及它参考过的单词 token 的提示(wid)。坐标本身它并不生成。引擎会先把这个值的文本,与 Vision OCR 实际在页面上检测到的字符(symbol)逐字符比对,再把框放到找到的那个真实像素位置上。在此基础上,给每个值附上 match_ratio(一致度,0~1.0)。1.0 表示所有字符都在原件上找到了,0.85 以上视为可信匹配。语言模型给的 token 提示,有时会用来辅助性地修正所找到 token 的位置,但不会盲信 ── 尤其在相同值排成一列时提示容易飘,所以会用整列的归类、整行的一致性来校验,一旦对不上就退回逐字符比对的结果。坐标以 xmin / ymin / xmax / ymax 的 0~1000 归一化网格(0–1000 normalized)(0,0=左上,1000,1000=右下)返回,因此不依赖图片的分辨率。说到底,不是「AI 不会错」,而是「把每一个值都对照原件,并用数值表明它匹配到了什么程度」。
导出 CSV,导入会计软件
核对完毕,就把这张表导出成 CSV。表头由标量列+数组的子列(像 明细.商品名 那样展开)构成,明细按一件商品=一行纵向展开。如果有手动改过的单元格,会以那个修改后的值优先。
再说乱码这件事。导出的 CSV 是 带 UTF-8 BOM 的,所以用 Excel 打开也不会乱码。这正是它能从根上不发生「Excel 乱码」的原因。
导入 freee/マネーフォワード/弥生
导出的 CSV,从各会计软件的 「CSV 导入」功能读入即可。它走的不是官方 API 对接,而是通用的 CSV 导入。也正因如此,它不太依赖会计软件那边的版本或套餐,只要把列对应关系(映射)配好,就能直接导进去。
月末攒下的收据:拍照 → 一张表 → 一个 CSV → 一次性导入软件。原本手动录入的时间,变成了核对的时间。
如果手头不是图片而是扫描 PDF,可以一并看看扫描 PDF 转 Excel 的步骤。想用 API 自动处理发票、送货单的话,发票・送货单 OCR API 的文章会有帮助。
PDF 形式的收据,也能直接处理
邮件里收到的收据是 PDF,还复制不了——这也是常见的卡点。把 PDF 拖进应用,每一页都会先自动转成图片,再去跑 OCR。所以「PDF 没法复制」这堵墙,你这边完全不用操心。多页 PDF 会逐页处理,结果一并堆进表里。
价格透明,失败不计费
- 每张 ¥10 的按量计费(按 call/image 计)。
- 失败不计费 ── 没出结果的图片不收钱,引擎错误会自动退款。
- 免费额度每月 100 张(无需信用卡,每月重置)。
- 想包月用就选 Pro $39/月。
如果只是「先拿这个月的收据试试看」,在免费额度内就能直接跑起来。
收据、发票的电子化,在配合发票制度(インボイス制度)和电子账簿保存法(電子帳簿保存法)建立合规流程的实务中很有用。每个值都能带着 match_ratio 回溯到原件的哪个位置读出来,这一点有助于公司内部核查和确认。不过本文不构成会计或法务建议,也不保证满足任何法律要求或通过认证。制度合规的最终判断,请务必向顾问税理士等专业人士确认。
月末的收据,拍张照就收工
- 把列(schema)定义一次除了店名、分店名、日期、合计,把明细定义成数组(array)类型的列,子字段为「商品名、单价、数量」。这样之后所有收据都会按同一形态整理成一行。
- 把收据拍照/扫描后上传手机拍照或扫描都行。PDF 拖进来后,每一页会先自动转成图片再做 OCR。它会按一张=一行读取,列自动填满。
- 用原件核对可疑的值点一下单元格,原件图片上读取这个值的位置(bbox)就会高亮。match_ratio 低于 0.85 的值是匹配偏弱的信号,应优先确认、修改。手动改过的值会与原始 OCR 值分开保存。
- 导出成 CSV把表导出成 CSV。明细等数组列会像「明细.商品名」那样展开,按一件商品=一行纵向展开。导出带 UTF-8 BOM,所以用 Excel 打开日文也不乱码。
- 用会计软件的 CSV 导入功能导入从 freee、マネーフォワード、弥生等的「CSV 导入」功能读入。它不是官方 API 对接,而是通用的 CSV 导入,所以把列的对应关系配好就能直接导进去。