如何從圖片中提取表格資料並轉存為 CSV 檔案
將表格、訂單或出貨單的照片,轉換為乾淨的 CSV 檔案。了解 space-ocr 如何判讀項目明細,以及逐列驗證如何把需要人工核對的數值標示出來。
將掃描表格的資料輸入試算表,是件傳統又繁瑣的工作。您可能有一張清晰的出貨單或採購單圖片,上面列滿了詳細項目,但這些終究只是像素。接下來的步驟通常是枯燥的人工資料輸入,逐一將品項、數量和價格複製到新的儲存格中。這個過程不僅緩慢,一個小小的打字錯誤就可能毀了整份資料。

一個更好的方法,是把表格結構定義為一份 schema(欄位結構)。與其單純抓取一大塊文字,不如先宣告您需要的欄位。重複出現的項目明細區塊定義為 array 類型欄位,並在它的子欄位(children)中排列各欄;固定是數字的欄位則直接宣告類型。
{
"name": "items",
"type": "array",
"children": [
{ "name": "name", "type": "string" },
{ "name": "qty", "type": "integer" },
{ "name": "price", "type": "number" },
{ "name": "amount", "type": "number" }
]
}您不需要指定列數,回傳多少列由頁面本身決定。每一列都以帶索引的路徑回傳(values.items[0]、values.items[1] ……),同一條路徑也就是逐值座標與驗證對照表的鍵 cells["items[0].price"]。宣告類型不會改變提取到的數值,因為類型不會傳給模型;它增加的是與 values 並列的第二層確定性資料 data.normalized。
即使是內容密集、充滿重複數值的表格,這種方法也同樣有效。系統會先使用大型語言模型來初步提取文字,但這只是第一步。對於每一個數值,例如品名為「刻みたくあん」或單價為「580」,系統都會進行交叉驗證。引擎會把語言模型的判讀與文件的欄位結構比對,並與頁面上原始偵測到的 OCR 符號逐字匹配。當某個數值滑到相鄰的資料列時,該座標處的字元通常就對不上了,這個欄位不會被悄悄放行,而是記入待核對清單。
"data": {
"values": {
"items": [
{ "name": "刻みたくあん", "qty": "3",
"price": "580", "amount": "1,740" }
]
},
"cells": {
"items[0]": {
"box": { "xmin": 263, "ymin": 460,
"xmax": 738, "ymax": 523 },
"quad": [ { "x": 263, "y": 460 }, { "x": 738, "y": 460 },
{ "x": 738, "y": 523 }, { "x": 263, "y": 523 } ],
"verified": null, "review": null
},
"items[0].name": {
"box": {…}, "quad": […],
"verified": true, "review": null,
"evidence": { "text_match": true, "match_ratio": 1.0 }
},
"items[0].qty": { "box": {…}, "quad": […],
"verified": true, "review": null },
"items[0].price": {
"box": { "xmin": 693, "ymin": 460,
"xmax": 738, "ymax": 488 },
"quad": […],
"verified": false,
"review": { "reasons": ["text_mismatch"] },
"evidence": { "text_match": false, "match_ratio": 0.62 }
}
},
"review": {
"unit": "field",
"flagged": [
{ "path": "items[0].price", "reasons": ["text_mismatch"] }
]
},
"normalized": {
"items": [ { "qty": 3, "price": 580, "amount": 1740 } ]
}
}這份清單就是待辦佇列:data.review.flagged 會指名確切的路徑與理由,cells["items[0].price"] 則保留了當時比對的座標。但它並非萬無一失。相鄰列若印著相同的數字,框落在哪一列字元都能對上;兩個引擎若犯了同一個誤讀,也就沒有可供對照的一方。因此,逐字比對看不見的行列錯位,值得交給可宣告的規則:編碼欄位用 pattern,主檔已有的值用 enum,合理區間用 min 與 max。任何違反都會出現在同一份 review.flagged 之中。
最後一層是您自己的核算,而它屬於下游。由於 qty 宣告為 integer、金額欄位宣告為 number,data.normalized 中放的是解析後的數值("1,740" 即 1740),因此一列的核算只需一次乘法。
const { values, normalized, review } = data;
const rows = values.items.map((item, i) => {
const n = normalized.items[i];
const flagged = review.flagged.some((f) =>
f.path.startsWith(`items[${i}]`)
);
return {
name: item.name,
qty: n.qty,
price: n.price,
amount: n.amount,
// 需要人工核對的列:被標記,或這一列算不平
check:
flagged || n.qty == null || n.qty * n.price !== n.amount,
};
});無法解析的葉節點在 normalized 中回傳 null,失敗類型記在 cells[path].normalized.error,並以 type_mismatch 出現在待核對清單裡。計算時取用 normalized,顯示時保留 values——帶著座標與驗證的是 values 這一側。
每一個數值都會與它所在的那一頁對照。系統把模型的判讀與該處實際偵測到的 OCR 符號逐字匹配,匹配了多少記錄在 evidence.match_ratio;達到 0.85 或更高即為高信賴度匹配。結論本身是 verified,它是 review 的鏡像:只要有任何標記就是 false,比對跑過而沒有標記則是 true,沒有可比對的對象(例如整列的聯集框)則是 null。座標由匹配到的符號推導為 box 與 quad,並相對 data.image 標準化為 0–1000 的範圍。這是數值來自何處的證據,而不是它就是您要的那個數值的證明;沒有通過核對的數值,都列在 review.flagged 裡。
費用根據用量計算,處理每張圖片為 $0.05。您的帳戶每月包含 100 次免費掃描。如果因任何原因提取失敗,我們不會收取任何費用。
- 定義工作表結構建立一個新的工作表並定義您的欄位。對於項目明細,請使用「array」類型,並為其新增如品名、數量、價格等子欄位。
- 上傳您的圖片透過拖放或使用 API 將表格圖片上傳至該工作表。
- 檢視提取的資料圖片將會根據您定義的結構進行處理。表格中的每個項目明細都會以結構化資料列的形式出現在工作表中。
- 視情況修正點擊任一儲存格,即可在圖片上看到對應的區域。您可以直接在網格中手動修正任何數值。
- 匯出為 CSV點擊「匯出」按鈕並選擇 CSV。您的表格資料(包含所有項目明細)將會下載為一個乾淨、結構化的檔案。