開發緣起
近期因為工作轉換,我成為 Business Development。無論參加會議、活動或展會,每天都會收到一疊名片。尤其是在展會期間,還需要回覆感謝信、後續追蹤等,名片整理變成一個非常現實、必須解決的麻煩。
市面上雖然已有許多名片辨識工具,但以我每月 40~60 張的使用量來說,花一百多元訂閱一年實在不划算,更別說多數服務都需要綁一年,對於我的季節性需求顯得有些浪費。
於是我開始思考:「既然大家都在 vibe coding,我是不是也能自己打造一套好用又免費的名片掃描器?」
第一版我按照 ChatGPT 的建議,採用 Google 表單 + Google Sheet 搭建出 Version 1。雖然成功跑起來,但原本考量資安風險,我只用 Google Drive 內建 OCR,辨識率一直無法提升。
和大神好友討論後,我決定導入 Gemini 做分類判讀,同時把上傳入口改為 Google Drive。這個關鍵調整,讓整個系統升級。以下也把整套流程分享給大家。
為什麼選擇在 Google 生態系下打造名片掃描系統
理由很簡單:
1. 公司本身就使用 Google Workspace。
2. Google Sheet 天生適合與同事共享資料。
一開始我其實抗拒導入 Gemini,因為擔心 API 計價模式會帶來額外費用。但研究後發現:
👉 Google 提供開發者免費額度,而我的使用量根本不可能用完。
👉 若沒有綁定信用卡,用超過也最多是被鎖,不會被收錢。
所以這個系統符合我以下的需求
1. 免費
2. 便於使用及分享資料
這個名片辨識系統可以做到甚麼
1. Google Drive 自動辨識邊框:用手機拍照上傳時,Drive 會自動裁切與偵測邊框,大幅省下手動調整的時間。
2. 自動重新命名並歸檔:上傳後,系統會依照辨識到的姓名重新命名檔案,並把新連結存入 Google Sheet。
3. 自動擷取名片資訊:姓名、Email、手機等資訊會直接填入試算表,後續追蹤與寄信都超方便。
最終你可以得到如下格式的完整資料:
(以下為匿名示意)
雖然是免費神器,但還是有幾項限制:
1. 需要手動填入 Gemini API、資料夾 ID、Sheet ID。
雖然是一次設定,但對非工程背景的人來說仍需耐心。
2. Apps Script 看起來像寫程式,會讓人害怕。
不過你完全不需要手寫,我大部分也是靠 vibe coding
一起動手,Vibe 起來
Googel sheet + Apps Script 範本請查看以下:
https://docs.google.com/spreadsheets/d/1TiBcJqYnRTfjjUUZ614i4UuLwbGgXZf70CPpD0XMaTg/edit?usp=sharing
程式碼請透過以下方式加入:
在「檔案」的地方,貼上程式碼。開頭有幾個地方需依照不同的資料夾設定

// ================= 設定區 (請修改這裡) =================
const API_KEY = '您的_GEMINI_API_KEY'; // 請貼上您的 API Key
const SOURCE_FOLDER_ID = '您的_待處理資料夾_ID';
const DEST_FOLDER_ID = '您的_已歸檔資料夾_ID';
const SHEET_ID = '您的_Google_Sheet_ID';
const SHEET_TAB_NAME = '工作表1'; // 如果您有改名,請修正這裡
// ================= 主程式:流程控制器 (已更新欄位對應) =================
function processBusinessCards() {
const sourceFolder = DriveApp.getFolderById(SOURCE_FOLDER_ID);
const destFolder = DriveApp.getFolderById(DEST_FOLDER_ID);
const sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(SHEET_TAB_NAME);
const files = sourceFolder.getFiles();
while (files.hasNext()) {
const file = files.next();
const mimeType = file.getMimeType();
// 只處理 JPG/PNG/PDF
if (mimeType === 'image/jpeg' || mimeType === 'image/png' || mimeType === 'application/pdf') {
try {
Logger.log(`正在處理檔案: ${file.getName()}`);
// --- 步驟 1: 呼叫 Gemini,一站式完成 OCR 和結構化分類 ---
const analysis = getOcrAndClassification(file);
if (analysis) {
// 解構所有欄位
const { chinese_name, english_name, company, tel, mobile, email, address, raw_text } = analysis;
const dateStr = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm');
// 取得檔案連結
const fileLink = file.getUrl();
// ----------------------------------------------------------------
// 關鍵修正:電話/手機號碼前加單引號 ('),強制 Sheets 以純文字顯示
// ----------------------------------------------------------------
const telFormatted = tel ? `'${tel}` : "";
const mobileFormatted = mobile ? `'${mobile}` : "";
// 備註/OCR 欄位 (J 欄) 內容組合:模擬您提供的格式
const notes =
raw_text;
// 2. 寫入 Google Sheet (注意:欄位 A 到 J 總共 10 個,必須與您的 Sheet 順序完全匹配)
sheet.appendRow([
dateStr, // A: 掃描日期
chinese_name, // B: 姓名 (中文)
english_name, // C: 英文名
company, // D: 公司 (新增)
telFormatted, // E: 市話 (已加單引號)
mobileFormatted, // F: 手機 (已加單引號)
email, // G: Email
fileLink, // H: 圖片連結 (新增)
"", // I: 名片來源 (手動留空)
notes // J: OCR (備註欄位)
]);
// 3. 檔案更名 (格式: 姓名_英文名_原始檔名)
const namePart = (chinese_name || english_name || file.getName()).replace(/\s/g, '');
const newFileName = `${namePart}_${file.getName()}`;
file.setName(newFileName);
// 4. 移動檔案到「已歸檔」資料夾
file.moveTo(destFolder);
Logger.log(`成功!檔案已更名為: ${newFileName} 並歸檔。`);
}
} catch (e) {
Logger.log(`處理檔案 ${file.getName()} 時發生錯誤: ${e.toString()}`);
}
}
}
}
// ================= 輔助函式:Gemini 一站式處理 (保持不變) =================
/**
* 呼叫 Gemini API,要求回傳原始 OCR 文字和結構化分類的 JSON。
* 此函式負責提取所有 8 個關鍵資訊欄位。
*/
function getOcrAndClassification(file) {
const MODEL_NAME = 'gemini-2.5-flash';
const url = `https://generativelanguage.googleapis.com/v1beta/models/${MODEL_NAME}:generateContent?key=${API_KEY}`;
const blob = file.getBlob();
const base64Data = Utilities.base64Encode(blob.getBytes());
const mimeType = blob.getContentType();
// 強化指令:確保所有欄位都被提取
const instruction = `分析此名片圖片。請執行以下兩項任務:
1. 提取圖片中所有的可讀文字,放在 "raw_text" 欄位中。
2. 從這些文字中,**必須**結構化提取出以下所有欄位:
- "chinese_name" (中文姓名): **不包含任何空格** (例如: 林艾咪)。
- "english_name" (英文姓名)。
- "company" (公司名稱)。
- "tel" (市話/總機): 提取固網電話號碼。
- "mobile" (手機): 提取手機號碼。
- "email" (電子郵件)。
- "address" (地址)。
請直接回傳單一 JSON 格式,不要有任何 markdown 符號或解釋。所有文字請以繁體中文優先。
如果找不到特定資料,請將該欄位的值留空("")。`;
const payload = {
"contents": [{
"parts": [
{
"text": instruction
},
{
"inline_data": {
"mime_type": mimeType,
"data": base64Data
}
}
]
}]
};
const options = {
"method": "post",
"contentType": "application/json",
"payload": JSON.stringify(payload),
"muteHttpExceptions": true
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
if (json.candidates && json.candidates[0].content && json.candidates[0].content.parts) {
let text = json.candidates[0].content.parts[0].text;
text = text.replace(/```json/g, '').replace(/```/g, '').trim();
try {
const result = JSON.parse(text);
// 確保中文姓名欄位沒有空格 (雙重保險)
if (result.chinese_name) {
result.chinese_name = result.chinese_name.replace(/\s/g, '');
}
return result;
} catch (e) {
Logger.log("Gemini JSON 解析失敗: " + text);
return { chinese_name: "分類失敗", english_name: "", company: "", tel: "", mobile: "", email: "", address: "", raw_text: text || "JSON 格式錯誤,請手動檢查原始文字。" };
}
} else {
Logger.log("Gemini API 回傳錯誤或無內容: " + JSON.stringify(json));
return { chinese_name: "API錯誤", english_name: "", company: "", tel: "", mobile: "", email: "", address: "", raw_text: "API 通訊失敗,無 OCR 內容。" };
}
}
個人設定
1. 先取得 Gemini API
前往 Google AI Studio https://aistudio.google.com/ ,在右側選單選擇,get API Key;
選擇 Create API key 之後,系統會給一套 API,按複製貼入 第一行
const API_KEY = '您的_GEMINI_API_KEY'; // 請貼上您的 API Key
建立兩個資料夾,一個可以叫做「名片上傳_Original」,這是主要上傳名片的地方
資料夾的連結會像: https://drive.google.com/drive/folders/[資料夾ID]
folders/之後,一長串的英數夾雜字串,就是這個資料夾的 ID,這個 ID 放入:
const SOURCE_FOLDER_ID = '您的_待處理資料夾_ID'
另外可以再新增一個「名片_歸檔」,名片完成OCR之後可以重新命名,上傳在這個資料夾
ID 放入 const DEST_FOLDER_ID = '您的_已歸檔資料夾_ID';
另外,以下兩個也請依同樣方式調整
const SHEET_ID = '您的_Google_Sheet_ID';
const SHEET_TAB_NAME = '工作表1'; // 如果您有改名,請修正這裡
設定觸發器
這是用來設定甚麼時候要啟動辨識,設定方法如下:
1. 在 Apps Script 編輯器左側,點擊 「觸發條件」(時鐘圖示)。
2. 點選 「新增觸發條件」。
3. 設定內容如下:
○ 選擇要執行的功能: processBusinessCards
○ 選取活動來源: 時間驅動 (Time-driven)
○ 選取觸發條件類型: 分鐘計時器 (Minutes timer)
○ 選取分鐘間隔: 每 5 分鐘 (建議)
設定完成,可以試著上傳名片看一下效果。
設定手機上傳捷徑
如果使用 Android 手機,可以另外設定一個手機資料夾捷徑,一拍照就可以上傳指定資料夾。
(iPhone 請自行測試)
設定步驟:
1. 開啟Google Drive應用程式: 確定您的手機上已安裝並登入 Google Drive。
2. 找到目標資料夾: 導航至您建立的 「名片_待處理」 資料夾。
3. 點擊選項菜單: 在該資料夾名稱旁邊,點擊 「三個點」 的選項圖示 (⋮)。
4. 建立捷徑: 在彈出的菜單中,選擇 「新增到主畫面」 (Add to Home screen)。
5. 確認添加: 系統會提示您確認,點擊 「新增」 或 「自動新增」。
完成後,手機桌面上就會出現一個名片資料夾的圖示。未來只需點擊該圖示,即可直接進入上傳介面。
如果想修改欄位怎麼辦?
只要把:
• 目前的程式碼
• Sheet 欄位截圖
一起丟給 ChatGPT,說明你想調整欄位順序或新增欄位,它就會自動調整語法給你。
我在開發過程中也是調整了好幾次,全部都靠 AI 幫忙。
如果在名片掃描過程中,發現辨識結果不理想,我發現最快的方式就是把這些名片的圖片檔,以及辨識結果一起丟給 AI,跟他說明狀況,並請他修改程式碼。
一天可以辨識多少名片
免費額度是我一開始就非常關心的事情,此部分有來回跟AI溝通詢問了好幾次。
大概的討論結果是: 免費額度通常受到兩個核心指標限制:
1. RPM (Requests Per Minute): 每分鐘請求數(通常限制在 60 次/分鐘左右)。
2. RPD (Requests Per Day): 每日請求總數,對於多模態(視覺)請求,額度通常在數千次以上。
實際使用後,也可以利用 Usage and billing 裡面的視窗看使用量,看是否會超過。
不過因為我並沒有輸入任何可扣款的信用卡資訊,相信如果用超過只是直接被鎖起來。
另外,因為我實際使用量並不大,估計一個月處理的名片數量在一百張以內;
只要透過分次上傳,就不太可能被扣到錢。
第一次 Vibe
這套系統從發想到完成,斷斷續續花了約一週。第一次看到它成功自動 OCR、重新命名、寫入欄位的瞬間,真的超級開心。
更重要的是:
我用 AI 打造了一個完全符合自己需求、免費又堅固的自動化工具。
如果你也在名片地獄中掙扎,不妨一起 Vibe 起來吧!
留言
張貼留言