space ocr
指南文章价格文档

用收据 OCR 转成 CSV,再导入会计软件

收据小票手动录入、Excel 打开乱码、明细全挤进一个单元格、PDF 没法复制——把这些月末苦活替换成“拍照→定义好列→一张=一行→导出 CSV→导入 freee/Money Forward/弥生”的实操指南。明细用数组列展开成一件商品=一行,UTF-8 BOM 杜绝乱码,每个值都带坐标(box/quad)和待核对标记,可与原件核对。每点数 $0.05、失败不计费、每月 100 点数免费额度。

月末。从钱包和抽屉里翻出来的一沓收据和小票,得一张张手动录进会计软件:敲店名、敲日期、敲合计,再换下一张。攒到三十张,注意力早就散了,金额多打一位数都未必发现。“手动录收据”之所以折磨人,不在于量大,而在于它明明很简单、却又错不得。

想着扫描后贴进 Excel 吧,结果又撞上另一堵墙:打开 CSV,中文、日文全是乱码;小票上的明细(买了哪些商品的清单)一换行就被压进同一个单元格;用 PDF 发来的收据连复制都做不到。到头来还是把原件摊在屏幕旁边,盯着一行行重新敲一遍——这么折腾,数字化到底图个啥。

这篇文章,就是要把这套活替换成 “拍下收据 → 把列定义好一次 → 上传后一张=一行 → 导出 CSV 并导入 freee/Money Forward/弥生” 的实操指南。它不是产品说明书,请当成“月末那一沓收据该怎么收拾”的经验来读。

先上手试试(无需上传,10 秒搞定)

下面这个示例,是把两张真实收据跑了一遍 OCR 的结果。把鼠标移到任意一个字段(店名、日期、合计、明细)上,它是从图片哪个位置读出这个值的,就会在原件上高亮出来。不用上传,也不用登录。先来确认一下:读出来的值,是不是真的指向原件上的那个位置。

Receipts with extracted-field bounding boxes
Verified fields
KINSHO · 合計 2,045
ライフ · 合計 4,286

Each value with a box carries a verified on-page location — in data.cells[path], that is box + 4-point quad + evidence.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 个步骤

要做的事其实不多。下面按顺序用视频一步步看。

  1. 上传原图 ── 把拍好或扫好的收据,灌进事先定好的列(店名、日期、合计、明细…)。
  2. 在抽取表里确认 ── 一张变成一行,列自动填满。
  3. 用坐标与原件核对 ── 核对没通过的值会自动标成待核对,点一下那个单元格,原件上对应位置就会亮起,肉眼立刻核查。
  4. 导出 CSV ── 直接导出成能导进会计软件的 CSV。

先看步骤一:上传拍好/扫好的收据后,每张图片都会按你定义的列被结构化。

上传收据后,值会自动灌进 店名、日期、合计、明细 这些列里,一张就成了一行。

列(schema)只需定义一次

关键在于:一开始就把列定下来。 像“店名、分店名、日期、合计、明细”这样把想要的项目先定义好,之后上传的所有收据都会按同一套形态(同一 schema)整理成一行。哪怕各家店的排版千差万别,输出的行形态都一样。也正因如此,最后才能汇成一张 CSV。

“明细全挤进一个单元格”问题的真相

手工填 Excel 时最头疼的,就是小票上的明细(买了哪些商品的清单)。一张收据上有十行商品,你要么把它塞进一个单元格,要么手动拆成十行。

在 space-ocr 里,明细被定义成数组(array)类型的列。导出 CSV 时,这个数组列会 展开成 明细.商品名 明细.单价 明细.数量 这样的子字段,按一件商品=一行纵向铺开。也就是说,它不是“挤进一个单元格”,而是一开始就以会计、汇总都能直接处理的行形态输出。

定义列的时候,还可以顺手把值的类型也定下来。日期用 date,合计、单价、数量用 number,像收据编号这种格式固定的值就留在 string 上再加一条 pattern。类型和格式都不会传给模型,所以抽出来的值仍然是原件上印的写法,导进 CSV 的也是这个写法。多出来的是核对材料:按声明的类型解析不了的值会标上 type_mismatch,不符合格式的会标上 pattern_mismatch,能解析的则会在值旁边附上可直接参与计算的归一化值(normalized)。required 只加在原件必定会印出来的项目上 ── 返回为空时会以 missing 进入待核对清单。取值范围固定的列还可以用 enum(见 API 文档)。

列的定义(明细为数组类型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "columns": [
    { "name": "店舗名", "type": "string" },
    { "name": "支店名", "type": "string" },
    { "name": "日付",   "type": "date",   "required": true },
    { "name": "合計",   "type": "number", "required": true },
    { "name": "領収書番号", "type": "string", "pattern": "^[A-Za-z0-9-]{4,}$" },
    {
      "name": "明細",
      "type": "array",
      "children": [
        { "name": "商品名", "type": "string" },
        { "name": "単価",   "type": "number" },
        { "name": "数量",   "type": "number" }
      ]
    }
  ]
}

可疑的值,点一下就跳到原件去核对

读取完成后,你不必逐个重看。引擎会把每个值和原件上的文字比对,只在核对没通过的值上留下待核对标记。点一下带标记的单元格,原件图片上“就是从这里读出来的”那个位置就会高亮,视线一下子被带过去。整沓收据的抽查,只要顺着标记处理就够了。

用 API 接收时也是同一套。需要复核的值都汇总在 data.review.flagged 里,每条带着路径和理由(reasons);拿这个路径去查 data.cells,就能取到与坐标轴平行的 box,以及随页面倾斜的四点 quad。一个值到底来自图片的哪里,人和系统事后都能回溯——也就是说,可审计。

点一下单元格,原件图片上读取这个值的位置 就会亮起。和原件比对的核查一瞬间就完成。
✓ Verified

这些坐标,不是 AI 凭“应该在这儿吧”给出的数字。 语言模型返回的,只有值的文本,以及它参考过的单词 token 提示(wid),坐标本身它并不生成。引擎会先把这个值的文本,和 Vision OCR 实际在页面上检测到的字符(symbol)逐字比对,再把框放到找到的那个真实像素位置上。随后,它会把有多少字符在原件上被找到,作为 match_ratio(一致度,0~1.0) 记进佐证(evidence)里。1.0 表示所有字符都在原件上找到了,引擎把 0.85 以上视为可信匹配。这个数字不是给人当合格线读的 ── 比对没通过的值,引擎自己会打上待核对标记。语言模型给的 token 提示,有时会用来辅助修正找到的 token 的位置,但不会盲信 ── 尤其当相同的值排成一列时提示容易飘,所以会用整列的归类和整行的一致性来校验,一旦对不上就退回逐字比对的结果。坐标以 xmin / ymin / xmax / ymax 的 0~1000 归一化网格(0,0=左上,1000,1000=右下)返回,因此不依赖图片的分辨率。说到底,重点不是“AI 不会错”,而是“把每一个值都对照原件,把没对上的摆到看得见的地方”。

导出 CSV,导入会计软件

核对完毕,就把这张表导出成 CSV。表头由标量列+数组的子列(像 明细.商品名 那样展开)构成,明细按一件商品=一行纵向展开。如果你手动改过某些单元格,会以你修改后的值为准。

再说乱码这件事。导出的 CSV 带 UTF-8 BOM,所以用 Excel 打开也不会乱码。这正是它从根上不会出现“Excel 乱码”的原因。

把表导出成 带 UTF-8 BOM 的 CSV。明细展开成一件商品=一行,用 Excel 打开也不乱码。

导入 freee/Money Forward/弥生

导出的 CSV,从各会计软件的 “CSV 导入”功能读入即可。它走的不是官方 API 对接,而是通用的 CSV 导入。也正因如此,它不太依赖会计软件那边的版本或套餐,只要把列的对应关系(映射)配好,就能直接导进去。

月末攒下的收据:拍照 → 一张表 → 一个 CSV → 一次性导入软件。原本手动录入的时间,就变成了核对的时间。

如果手头不是图片而是扫描 PDF,可以一并看看扫描 PDF 转 Excel 的步骤。想用 API 自动处理发票、送货单的话,发票、送货单 OCR API 的文章会有帮助。

PDF 形式的收据,也能直接处理

邮件里收到的收据是 PDF,还复制不了——这也是常见的卡点。把 PDF 拖进应用,每一页都会先自动转成图片,再去跑 OCR。所以“PDF 没法复制”这堵墙,你这边完全不用操心。多页 PDF 会逐页处理,结果一并堆进表里。

价格透明,失败不计费

  • 每点数 $0.05 的按量计费(按 call/image 计)。
  • 失败不计费 ── 没出结果的图片不收钱,引擎错误会自动退款。
  • 免费额度每月 100 点数(无需信用卡,每月重置)。
  • 导出 CSV 计 1 点数 ── 同一份数据再次下载不另计费,Starter / Pro 方案的下载本身就免费。
  • 想包月用就选 Pro $39/月。

如果只是想“先拿这个月的收据试试看”,在免费额度内就能直接跑起来。

Why it matters

收据、发票的电子化,在配合发票制度(インボイス制度)和电子账簿保存法(電子帳簿保存法)建立合规流程的实务中很有用。每个值都能带着坐标回溯到原件的哪个位置读出来,核对没通过的值还会留下待核对标记,这一点有助于公司内部核查和确认。不过本文不构成会计或法务建议,也不保证满足任何法律要求或通过认证。制度合规的最终判断,请务必向顾问税理士等专业人士确认。

月末的收据,拍张照就收工

  1. 把列(schema)定义一次
    除了店名、分店名,把日期声明成 date、合计声明成 number、收据编号声明成带 pattern 的 string,再把明细定义成数组(array)类型的列,子字段为商品名、单价、数量。这样之后所有收据都会按同一形态整理成一行。
  2. 把收据拍照/扫描后上传
    手机拍照或扫描都行。PDF 拖进来后,每一页会先自动转成图片再做 OCR。它会按一张=一行读取,列自动填满。
  3. 用原件核对可疑的值
    点一下单元格,原件图片上读取这个值的位置就会高亮。核对没通过的值会自动标成待核对,请从带标记的值开始确认、修改。手动改过的值会与原始 OCR 值分开保存。
  4. 导出成 CSV
    把表导出成 CSV。明细等数组列会像“明细.商品名”那样展开,按一件商品=一行纵向展开。导出带 UTF-8 BOM,所以用 Excel 打开日文也不乱码。
  5. 用会计软件的 CSV 导入功能导入
    从 freee、Money Forward、弥生等的“CSV 导入”功能读入。它不是官方 API 对接,而是通用的 CSV 导入,所以把列的对应关系配好就能直接导进去。
对日文/中文的收据小票支持得好吗?
支持。语言完全自动识别,日文、英文、中文、韩文等用同一个引擎处理,不需要手动指定语言。全角半角、CJK 空格、混合文字也会归一化后读取。只要把店名、日期、合计、明细这些项目定义成列,日文收据就能直接结构化成一行。
PDF 形式的收据也能转成 CSV 吗?
能。把 PDF 拖进应用,每一页都会先自动转成图片再做 OCR,所以哪怕是复制不了的 PDF,你也不用在本地另做转换。多页 PDF 会逐页处理,结果堆进表里之后就能导出成 CSV。
导出的 CSV 能直接导入 freee、Money Forward、弥生吗?
从各会计软件的“CSV 导入”功能读入即可。它不是官方 API 对接,而是通用的 CSV 导入。CSV 带 UTF-8 BOM,所以用 Excel 打开也不乱码,把列的对应关系(映射)配好就能导入。需要注意,能否导入以及具体步骤,取决于各软件的规格和套餐。
读取的精度有多可靠?
具体的精度数值会受环境和原件状态影响,我们不下断言;真正可靠的是每个值都能逐一验证这一点。读出来的值会自动和原件上的 OCR 字符比对,只有没通过的值才会带着理由被标成待核对(该位置读到的文字与值不一致、必填项为空、不符合你声明的类型或格式等)。坐标不是语言模型生成的数字,而是把值的文本与原件的 OCR 字符比对后得到的。点一下带标记的单元格,原件上对应位置就会高亮,所以只挑这些值用肉眼快速确认、修改即可。不过两套引擎有可能在同一个误读上取得一致而不留标记,所以请配合你自己的业务规则(比如把合计重算一遍)一起使用。
个人信息和原件数据安全吗?读出来的值能验证吗?
所有值都会连同读取坐标(box/quad)和核对结果一起返回,便于事后回溯它是从原件哪个位置读出来的;没通过核对的值会带上待核对标记,而不是悄悄放行。上传的图片从空间里删除时,保存的原件也会一并删除。价格是每点数 $0.05 的按量计费,失败不计费,免费额度每月 100 点数、无需信用卡即可开始。要处理的单据中有收据等含个人信息的内容,请按公司内部的处理规范使用。

收据的手动录入,这个月就毕业吧

免费额度每月 100 点数、无需信用卡。拍照导出 CSV,直接导入 freee/Money Forward/弥生。

相关