GASとGemini APIで領収書を自動でスプレッドシートに記録する

GASとGemini APIで領収書を自動でスプレッドシートに記録する
目次

注意事項

  • 本記事の内容は試験的な実装であり、アイデアベースの検証です
  • 実務での利用を保証するものではありません
  • 実装についての責任は負いかねます。自己責任でご利用ください
  • AIの出力結果は常に検証が必要です

概要

領収書をスマホで撮影してGoogle Driveに保存するだけで、Gemini APIが内容を解析し、スプレッドシートに自動で記録する仕組みを作ります。

できること

  • 領収書画像をGoogle Driveの指定フォルダに置く
  • GASが定期的にフォルダをチェック
  • Gemini APIで画像から情報を抽出(日付、金額、店名など)
  • スプレッドシートに自動記録
  • 処理済みの画像は別フォルダに移動

事前準備

1. Google Driveにフォルダを作成

以下の2つのフォルダを作成します。

フォルダ名用途
未処理領収書画像を置くフォルダ
処理済み処理完了した画像の移動先

フォルダIDはURLから取得できます:

https://drive.google.com/drive/folders/【ここがフォルダID】

2. Gemini APIキーの取得

Google AI Studio からAPIキーを取得します。

注意: APIキーは外部に漏れないよう厳重に管理してください

3. スプレッドシートの準備

新規スプレッドシートを作成し、1行目にヘッダーを設定します:

ABCDEFGHIJ
日付科目金額税金店名仕入参照先支払い方法会社名インボイス

GASのコード

スプレッドシートの「拡張機能」→「Apps Script」を開き、以下のコードを貼り付けます。

// ==========================================
// 初期設定:以下の3箇所をご自身の環境に合わせて書き換えてください
// ==========================================
const GEMINI_API_KEY = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
const UNPROCESSED_FOLDER_ID = 'ここに未処理フォルダのIDを貼り付けます';
const PROCESSED_FOLDER_ID = 'ここに処理済みフォルダのIDを貼り付けます';

// ==========================================
// メイン処理
// ==========================================
function processReceipts() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const sourceFolder = DriveApp.getFolderById(UNPROCESSED_FOLDER_ID);
  const targetFolder = DriveApp.getFolderById(PROCESSED_FOLDER_ID);

  // 未処理フォルダ内のファイルを取得
  const files = sourceFolder.getFiles();

  while (files.hasNext()) {
    const file = files.next();
    const mimeType = file.getMimeType();

    // ★変更点: 画像ファイルとPDFファイルを対象にする
    if (mimeType.startsWith('image/') || mimeType === 'application/pdf') {
      try {
        // Gemini APIでデータを解析
        const receiptDataArray = analyzeReceiptWithGemini(file);

        if (receiptDataArray && receiptDataArray.length > 0) {
          for (let i = 0; i < receiptDataArray.length; i++) {
            const receiptData = receiptDataArray[i];

            const rowData = [
              receiptData.date || "",           
              receiptData.category || "",       
              receiptData.amount || "",         
              receiptData.tax || "",            
              receiptData.storeName || "",      
              receiptData.purchase || "",       
              file.getUrl(),                    
              receiptData.paymentMethod || "",  
              receiptData.companyName || "",    
              receiptData.invoiceNumber || ""   
            ];

            sheet.appendRow(rowData);
          }

          file.moveTo(targetFolder);
          Utilities.sleep(2000);
        }
      } catch (e) {
        console.error("ファイル " + file.getName() + " の処理中にエラーが発生しました: " + e.message);
      }
    }
  }
}

// ==========================================
// Gemini API 呼び出し処理
// ==========================================
function analyzeReceiptWithGemini(file) {
  const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`;

  // ★変更点: 変数名を画像とPDFの両方に合うように base64Data に変更
  const base64Data = Utilities.base64Encode(file.getBlob().getBytes());
  const mimeType = file.getMimeType();

  // ★変更点: プロンプトに「PDF」という言葉を追加し、AIの認識精度を高める
  const prompt = `
    あなたは優秀な経理アシスタントです。添付された領収書または請求書のデータ(画像またはPDF)から、以下の情報を抽出し、JSON形式の「配列」で出力してください。
    Markdownの装飾(バッククォートなど)は一切含めず、純粋なJSONテキストのみを返してください。

    【重要な指示】
    - 消費税率が10%のものと8%のものが混在している場合は、それぞれを分けて2つのオブジェクトとして配列に格納して返してください。
    - 単一の税率しかない場合でも、1つのオブジェクトを含む配列として返してください。

    【抽出する項目とJSONキー】各オブジェクトに以下のキーを含めてください。
    - date: 日付 (YYYY/MM/DD形式)
    - category: 科目 (例: 消耗品費、交通費、会議費、接待交際費など。内容から推測してください)
    - amount: 対象となる税率の合計金額 (数字のみ)
    - tax: 対象となる税率の消費税額 (数字のみ。記載がない場合は推測せず空白にする)
    - storeName: 店名または発行者名
    - purchase: 仕入かどうか (仕入に関わるものの場合は "対象"、それ以外は空白にする)
    - paymentMethod: 支払い方法 (現金、クレジットカード、PayPayなど)
    - companyName: 支払い方法がクレジットカードの場合、領収書に記載されているクレジットカード番号の「下4桁の数字」のみを入れてください。クレジットカード払いではない場合、または記載がない場合は空白にしてください。
    - invoiceNumber: インボイス登録番号 (Tから始まる13桁の数字。記載がない場合は空白)
  `;

  const payload = {
    "contents": [
      {
        "parts": [
          {"text": prompt},
          {
            "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 jsonResponse = JSON.parse(response.getContentText());

  if (jsonResponse.error) {
    throw new Error(jsonResponse.error.message);
  }

  try {
    let text = jsonResponse.candidates[0].content.parts[0].text;
    text = text.replace(/```json/g, "").replace(/```/g, "").trim();
    return JSON.parse(text);
  } catch (e) {
    throw new Error("Geminiからの応答の解析に失敗しました。応答内容: " + (jsonResponse.candidates ? jsonResponse.candidates[0].content.parts[0].text : JSON.stringify(jsonResponse)));
  }
}

APIキーの安全な管理

APIキーはコードに直接書かず、スクリプトプロパティに保存します。

  1. Apps Scriptエディタで「プロジェクトの設定」(歯車アイコン)を開く
  2. 「スクリプト プロパティ」セクションで「スクリプト プロパティを追加」
  3. プロパティ名: GEMINI_API_KEY、値: 取得したAPIキー

トリガーの設定

定期実行するためにトリガーを設定します。

  1. Apps Scriptエディタの左メニューから「トリガー」(時計アイコン)を選択
  2. 「トリガーを追加」をクリック
  3. 以下のように設定
項目設定値
実行する関数processReceipts
イベントのソース時間主導型
時間ベースのトリガーのタイプ分ベースのタイマー(または時間ベース)
間隔任意(例: 1時間おき)

使い方

  1. 領収書をスマホで撮影
  2. Google Driveの「未処理」フォルダにアップロード
  3. トリガーが実行されると自動でスプレッドシートに記録
  4. 処理済み画像は「処理済み」フォルダに移動

注意点

項目内容
AIの解析精度手書きや不鮮明な領収書は正確に読み取れない場合があります
必ず目視確認自動記録された内容は必ず確認してください
API利用料Gemini APIには無料枠がありますが、大量処理時は料金を確認してください
税率の分離軽減税率(8%)と標準税率(10%)が混在する場合、別々の行として記録されます

まとめ

GASとGemini APIを組み合わせることで、領収書処理の自動化が実現できます。 ただし、AIの解析結果を鵜呑みにせず、必ず人の目で確認することをお勧めします。