終結名片手動輸入!用 Google Apps Script 搭配 Gemini AI 實現名片全自動化分類與歸檔,Vibe起來~

開發緣起

近期因為工作轉換,我成為 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 起來吧!

留言

這個網誌中的熱門文章

介紹演講者魏寶生董事長:一次關於讀書會準備、AI 與成長的紀錄

被打斷的藝術:在辦公室中,如何有禮貌地「打擾」別人?

把雜事交給 AI 日曆管理:Toki 在生活與工作上的應用