Courses
手作業でのデータ入力は生産性の敵です。請求書や予約確認、顧客フィードバックフォームなどの業務メールからデータを抽出している場合、従来のパース手法が悪夢のようなものであることはよくご存じでしょう。
これまで開発者は、請求書番号や請求金額のような情報を取り出すために正規表現(Regex)に頼ってきました。しかし Regex は非常にもろく、ベンダーがメールのテンプレートを少し変えたり、ランダムな改行が入ったりした瞬間に、パーサー全体が気づかれないまま壊れてしまいます。
このチュートリアルでは、生成 AI を使ってこの問題を解決する、完全自動・ヘッドレスのパイプラインの作り方を紹介します。未読の Gmail メールを読み取り、非構造化テキストを Gemini API に渡してインテリジェントに抽出し、完全に構造化された JSON データを Google スプレッドシートに直接記録する軽量スクリプトを書きます。
前提条件:
- Google アカウント
- JavaScript(Google Apps Script)の基礎知識
- Gemini API キー
Google Apps Script とは?
Google Apps Script は、JavaScript に基づくクラウド型の高速アプリケーション開発プラットフォームで、Google ドキュメント、スプレッドシート、スライドなどの Google Workspace アプリケーションを自動化・カスタマイズ・拡張できます。
プラットフォームは Google のサーバー上で直接動作するため、ソフトウェアのインストールやローカル環境の構築、インフラの管理は不要です。
Apps Script を使えば、開発者/ローコード開発者は Docs、Sheets、Slides、Drive などの Google Workspace ツールとプログラムでやり取りできます。カスタムメニューやボタンクリック、スケジュール(時間主導)で自動化を実行でき、Google Workspace マーケットプレイス向けのアドオンの構築と公開にも利用できます。
Google Apps Script の仕組み
Google Apps Script は、バックエンド構成や API 認証といった重作業を抽象化し、ロジックとデータに集中できるようにします。主な特徴は次のとおりです。
- クラウドネイティブなエディタ: ブラウザに組み込まれた Web ベースの IDE 上で、JavaScript コードの作成・デバッグ・デプロイを完結できます。
- 組み込みサービス: OAuth や複雑な API 呼び出しに苦労する代わりに、SpreadsheetApp、MailApp、DriveApp のような用意済みのグローバルオブジェクトを使って、Google サービスとネイティブに連携できます。
- イベント駆動トリガー: スクリプトはカスタムメニューボタンから手動実行できるほか、時間主導トリガー(例:毎日深夜)やイベント主導トリガー(例:ユーザーが Google フォームを送信したとき)で自動実行できます。
Google Apps Script のアーキテクチャ:Gmail から Sheets へのパイプライン
スクリプトを書く前に、このパイプラインでデータがどのように流れるかを整理します。アーキテクチャは、4 つの独立したコンポーネントが連携して動作します。
- Gmail(ソース): 標準の検索演算子(例:is:unread label:invoices)で受信トレイを検索することから始まります。スクリプトは該当するメールスレッドを抽出し、プレーンテキスト本文と添付の PDF ファイルを取り出します。
- Apps Script(コンピュート): オーケストレーション層として機能します。未読スレッドを反復処理し、添付ファイルを base64 データに整形し、エラー報告やバッチ処理のロジックを扱います。
- Gemini API(変換エンジン): Apps Script はマルチモーダルのペイロード(メール本文+PDF)を Gemini 2.5 Flash モデルへ送ります。API 呼び出し時に厳密な JSON レスポンススキーマを渡すことで、会話文ではなく、ベンダー名、合計金額、通貨などの正確な値を抽出するようモデルに指示します。
- Google スプレッドシート(宛先): Gemini が構造化済みの JSON を返すと、Apps Script は結果をパースし、1 つの配列にまとめ、setValues() を用いてアクティブなスプレッドシートの次の空行にバッチで挿入します。

なぜ今回の計算レイヤーとして Google Apps Script を使うのか?
最大の利点は、インフラに関するオーバーヘッドを完全に排除できることです。サーバーのプロビジョニングは一切不要で、メールの読み取りやスプレッドシートへの書き込みのために Docker コンテナを構成したり、複雑な OAuth フローを用意したりする必要もありません。
Apps Script は GmailApp や SpreadsheetApp といったサービスとネイティブに統合されているため、認証は Google 側で処理されます。コードを書いてエンドポイントをつなぐだけで済みます。
ステップ 1:Google Apps Script 環境を準備する
コードを書く前に、外部依存関係を準備します。Gemini API へのアクセスの確保、宛先スプレッドシートの作成、請求書メールを適切にタグ付けするための Gmail 受信トレイの構成が含まれます。
Gemini API キーを作成する
インテリジェントなデータ抽出を実現するには、Gemini API へのアクセスが必要です。
- 次へ移動します Google AI Studio にアクセスし、Google アカウントでサインインします。
- 左側のナビゲーションメニューで Get API key をクリックします。
- 続いて Create API key をクリックします。既存の Google Cloud プロジェクトにキーを作成するか、AI Studio に新規作成させるかを選べます。
- 生成された文字列をコピーします。
セキュリティのベストプラクティス: このキーを紛失しないようにするとともに、平文でコードにハードコーディングしないでください。現時点ではクリップボードや安全なメモに保管し、次のステップで Apps Script 環境内に安全に格納します。

Google スプレッドシートの準備とキーの安全な保管
抽出データを保存する場所が必要です。このシートは自動化スクリプトの起点にもなります。
1. データベースのヘッダーを設定する 空の Google スプレッドシートを新規作成します。1 行目に、これからスクリプトで定義する JSON スキーマに合わせて、以下の列ヘッダーを作成します。
- Date
- Vendor
- Invoice Number
- Total Amount
- Currency
- Items Summary
- Email Link
最上段を固定して、データが増えてもヘッダーが常に見えるようにします。

2. Apps Script にアクセスして API キーを保存する シートができたら、そこから直接 Apps Script エディタを開きます。これで、このスプレッドシートにコードが自動的にリンクされます。
- Google スプレッドシートで、上部メニューの 拡張機能 > Apps Script をクリックします。

- エディタが開いたら、左サイドバーの歯車アイコンをクリックしてプロジェクトの設定を開きます。

- 下部までスクロールし、スクリプト プロパティのセクションを見つけます。
- 次にスクリプト プロパティを追加をクリックします。
- 次の プロパティ に、正確に
GEMINI_API_KEYと入力します。 - 次の 値 に、先ほど AI Studio からコピーした API キーを貼り付けます。
- 最後にスクリプト プロパティを保存をクリックします。

後で記述するコードは、実行時にこの値を安全に取得し、認証情報を露出しません。
ステップ 2:Google Apps Script のコードを書く
Apps Script エディタを開いたまま、デフォルトのコードは削除してください。ここから自動化エンジンをブロックごとに構築します。
1. 設定と UI セットアップ
まずはグローバルな設定オブジェクトを定義します。これは、ハードコードされた変数を 1 か所にまとめるクリーンコードのベストプラクティスで、上部のブロックだけを編集すれば済むようになります。
あわせて onOpen() 関数を追加します。これは組み込みの Apps Script トリガーで、Google スプレッドシートにカスタムメニューを自動作成し、非技術者でもコードエディタを開かずに手動でスクリプトを実行できるようにします。
const CONFIG = {
searchQuery: "is:unread label:invoices",
model: "gemini-2.5-flash",
adminEmail: Session.getActiveUser().getEmail() // Automatically emails the script owner if it crashes
};
/**
* Creates a custom menu in Google Sheets so non-technical users can run the script manually.
*/
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('🤖 AI Automations')
.addItem('Fetch New Invoices', 'processInvoices')
.addToUi();
}
2. オーケストレーションのセットアップ
processInvoices() 関数はメインのオーケストレーターとして機能します。まず、先ほど設定したスクリプト プロパティから API キーを安全に取得します。
その後、組み込みの GmailApp サービスを使って受信トレイを検索します。
GmailApp.search(CONFIG.searchQuery) を呼び出すと、is:unread label:invoices という条件に一致するメールスレッドの配列を取得します。
該当があれば、各スレッドをループし、最新メッセージを取得、プレーンテキスト本文を抽出し、添付の PDF ファイルを取り出します。
function processInvoices() {
const apiKey = PropertiesService.getScriptProperties().getProperty("GEMINI_API_KEY");
if (!apiKey) {
throw new Error("GEMINI_API_KEY is missing! Please set it in Script Properties.");
}
const threads = GmailApp.search(CONFIG.searchQuery);
if (threads.length === 0) return;
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const batchData = [];
const processedThreads = [];
3. メールとマルチモーダル PDF の処理
メールスレッドをループする際、最新メッセージを取得します。しかし請求書はプレーンテキストで届くことは稀で、ほとんどが PDF 添付です。
Gemini 2.5 Flash はネイティブにマルチモーダル対応のため、スクリプトは latestMessage.getAttachments() をループして PDF を取得し、Base64 文字列へ変換し、inlineData として API ペイロードに直接渡します。
Gemini は PDF を視覚的に「読み取り」、テキストと同様にデータを抽出します。
for (let i = 0; i < threads.length; i++) {
const thread = threads[i];
const latestMessage = thread.getMessages().pop();
const emailBody = latestMessage.getPlainBody();
const emailDate = latestMessage.getDate();
const emailLink = https://mail.google.com/mail/u/0/#inbox/${thread.getId()};
const attachments = latestMessage.getAttachments();
try {
const geminiParts = [];
geminiParts.push({ text: "Extract invoice details from this email and any attached documents:\n\n" + emailBody });
// Add PDF attachments as base64 to the prompt
for (const attachment of attachments) {
if (attachment.getContentType() === "application/pdf") {
geminiParts.push({
inlineData: {
mimeType: "application/pdf",
data: Utilities.base64Encode(attachment.getBytes())
}
});
}
}
// Invoke Gemini API
const extractedData = callGeminiAPI(apiKey, geminiParts);
4. バッチ挿入とエラー処理
パフォーマンスを最適化するため、1 行ずつスプレッドシートに書き込むことはしません。
代わりに、抽出した JSON データを配列(batchData)に詰め込み、最後に setValues() でまとめて書き込みます。
try...catch ブロックにも注目してください。
もし Gemini が誤動作したり、特定のメールで失敗しても、スクリプト全体は落ちません。エラーを丁寧に捕捉し、MailApp.sendEmail() で管理者に即時通知して可観測性を高めます。
// Push to batch array instead of writing one-by-one
batchData.push([
emailDate,
extractedData.vendor || "Unknown Vendor",
extractedData.invoiceNumber || "N/A",
extractedData.totalAmount || 0.00,
extractedData.currency || "USD",
extractedData.itemsSummary || "N/A",
emailLink
]);
processedThreads.push(thread);
} catch (error) {
MailApp.sendEmail(CONFIG.adminEmail, "🚨 Invoice Pipeline Error", error.toString());
}
}
// Batch Write to Sheets
if (batchData.length > 0) {
const lastRow = Math.max(sheet.getLastRow(), 1);
sheet.getRange(lastRow + 1, 1, batchData.length, batchData[0].length).setValues(batchData);
}
// Mark all processed threads as read so they aren't parsed again
processedThreads.forEach(t => t.markRead());
}
最後に、処理済みの各メールに対して thread.markRead() を呼び出します。これにより、次回の実行時にはこれらのメールは完全に無視され、新着のみが対象になります。
5. Gemini API のヘルパー関数(JSON モード)
LLM を呼び出す際、開発者は「JSON ブロックを返して」といったプロンプトエンジニアリングを使うことがよくあります。しかし、モデルはしばしば(「承知しました。こちらがデータです: [JSON]」のような)会話的な前置きを返し、下流のコードパーサーがクラッシュする原因になります。
responseMimeType: "application/json" を設定し、厳密な responseSchema を提供することで、Gemini の REST API に対し、スプレッドシートの列ヘッダーと完全に一致する JSON オブジェクトのみを返すよう強制できます。
function callGeminiAPI(apiKey, partsArray) {
const url = https://generativelanguage.googleapis.com/v1beta/models/${CONFIG.model}:generateContent?key=${apiKey};
const responseSchema = {
type: "OBJECT",
properties: {
vendor: { type: "STRING" },
invoiceNumber: { type: "STRING" },
totalAmount: { type: "NUMBER" },
currency: { type: "STRING" },
itemsSummary: { type: "STRING" }
},
required: ["vendor", "totalAmount"]
};
const payload = {
contents: [{ parts: partsArray }],
generationConfig: {
responseMimeType: "application/json",
responseSchema: responseSchema
}
};
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());
return JSON.parse(jsonResponse.candidates[0].content.parts[0].text);
}
ステップ 3:Google Apps Script パイプラインを自動化する
コードは、生活を楽にしてこそ価値があります。
Google Apps Script は、対話的なボタン操作から、完全にヘッドレスなバックグラウンドの cron ジョブまで、強力な実行環境を提供します。
方法 1:スプレッドシートの UI からトリガーする
非技術系の経理チーム向けにこのツールを作る場合、毎日の請求書を処理するのに Apps Script のコードエディタを開きたくはないはずです。
コードに onOpen() 関数を含めたことで、スプレッドシートを開くたびに、Apps Script が Google スプレッドシートのインターフェースにカスタムメニューを自動で組み込みます。
- Google スプレッドシートに戻り、ページを更新します。
- 数秒待つと、上部メニューバーに新しいドロップダウン 🤖 AI Automations が表示されます。
- クリックして Fetch New Invoices を選択します。

- 初回実行時は、Google からスクリプトの認可(Gmail の読み取りや代理でのメール送信)を求められます。承認後、スクリプトがリアルタイムに実行され、抽出された JSON データが目の前で行に入力されていく様子を確認できます。
方法 2:時間主導トリガーによるヘッドレス自動化
真のお任せ運用を望むなら、ネイティブの時間主導トリガーを使って、バックグラウンドで自律的にパイプラインを実行できます。
- Apps Script エディタで、左サイドバーのトリガー(時計アイコン)をクリックします。

- 右下の トリガーを追加 をクリックします。

- 関数を processInvoices に設定します。
- イベントのソースを「時間主導」に変更し、1 時間ごと(例:6 時間ごと)の実行に設定します。
- 保存をクリックします。

ヘッドレスな AI パイプラインが、これで完全自動になりました。
コンピュータの電源が完全に切れていても、Google のサーバーが 6 時間ごとに起動して受信トレイをスキャンし、Gemini で請求書を解析し、Sheets にデータを記録して、再びスリープします。
Apps Script エディタの「実行」タブをクリックすると、バックグラウンド実行の成功・失敗をログで確認できます。
ステップ 4:AI データ抽出をテストする
コードを用意できたら、抽出パイプラインが期待どおりに動くかテストします。
Gemini API が異なるデータ形式をどう扱い、実際にどのようなレスポンスが返るのか、2 つの実践的なシナリオで見ていきましょう。
テスト 1:プレーンテキストメールからの抽出
まず、メール本文に直接埋め込まれた請求書でテストします。
- 件名を「Invoice INV-2023-88493」として自分宛てにメールを送ります。
- 本文には次のような非構造化テキストを書きます:「チーム各位、Q3 のソフトウェアライセンスとして Acme Corp に $450.00 USD を支払ってください。」
- メールに invoices ラベルを適用し、未読のままにしておきます。

- カスタムメニューからスクリプトを実行します。

この裏側で、Gemini はこの非構造化テキストを受け取り、JSON スキーマに厳密に従います。
会話的な前置きではなく、生のペイロードを返します。
レスポンスが完全に構造化された JSON なので、batchData.push() の配列は extractedData.totalAmount を難なく読み取り、6550 を複雑な Regex なしでそのままスプレッドシートに記録します。

テスト 2:PDF 添付からの抽出
次に、PDF ドキュメントを渡して Gemini 2.5 Flash の真価を試します。
- サンプルの PDF 請求書を作成またはダウンロードします(例:ProTech Cloud Services から $20,700.00 の請求)。
- PDF ドキュメントを自分にメールで送ります。メール本文は空でも構いません。
- メールにinvoices ラベルを適用し、未読にします。

- スクリプトを実行します。パイプラインは添付を検出して Base64 に変換し、Gemini がドキュメントを視覚的に読み取ってベンダー名と合計金額を抽出します。

ここで、このアーキテクチャの価値が発揮されます。
カスタムの OCR(光学式文字認識)ロジックを書いたり、別の PDF 解析ライブラリを管理したり、合計金額を見つけるための正規表現を書く必要はありませんでした。
LLM が非構造化の視覚データをネイティブに処理してくれます。
まとめ
業務ワークフローの自動化に、重いインフラの管理や複雑な認可フローとの格闘は不要です。
Google Apps Script と Gemini API を組み合わせることで、日常的に使っているクラウドエコシステム内だけで動作する、インテリジェントなサーバーレスのデータパイプラインを構築しました。
このアーキテクチャの強みはシンプルさにあります。サーバーをプロビジョニングしたり、Webhook を構成したり、外部サーバー上で安全な認証情報ファイルを管理したりする代わりに、Apps Script が文脈・認証・実行をネイティブに扱います。
一方で Gemini は、もろい正規表現や高価な OCR ソフトを不要にし、雑多なメール本文や非構造化 PDF を、単一の API 呼び出しで予測可能なデータベース行へと変換します。
LLM へのプログラム的アクセスで他に何が作れるかをさらに探求したい場合は、DataCamp の Introduction to APIs in Python をチェックするか、AI Agent Fundamentals コースでインテリジェントなワークフロー設計を深掘りしてください。