毎月の手作業をゼロに!GASとDrive APIのOCRで 請求書PDFをスプレッドシートに自動集計してみた
はじめに
クラスメソッドの原田です。
毎月こんな作業をしていませんか?
- PDFを開いて金額を確認
- 電卓で税込金額を計算
- Slackに部署ごとの金額を手打ちで投稿
- 「先月より増えたっけ?」と過去のSlackを遡って確認
弊社でも取引先経由の利用費PDFを毎月このような運用で管理していました。
「これ、自動化できそうだな」 と思い立ち、Google Apps Script(GAS)で解決してみました。
解決したかったこと
| 課題 | 改善後 |
|---|---|
| PDFを見ながら手計算・手入力 | OCRで自動読み取り・自動集計 |
| Slackへの手動投稿 | Slack用テキストを自動生成してコピペするだけに |
| 前月比が把握しづらい | 推移表で月次トレンドを可視化 |
| 担当者しか作業できない | 誰でもボタン1つで実行可能 |
システム構成
Googleドライブ(PDFフォルダ)
↓ PDFを配置するだけ
Googleスプレッドシート
↓ メニューから実行
Google Apps Script(AzureBillingApp)
↓ Drive API v2(OCR)
├── DBシート(データ蓄積)
├── Masterシート(部署マスタ)
├── 推移表シート(月次トレンド)
└── Slack用シート(投稿テキスト)
実装のポイント
1. Drive API v2のOCRでPDFをテキスト化
GASには標準でPDFを読み取る機能がありませんが、Drive API v2のOCR機能を使うことでPDFをGoogleドキュメントに変換し、テキストとして取得できます。
function extractTextFromPdf(fileId) {
const resource = {
title: "temp_" + new Date().getTime(),
mimeType: "application/vnd.google-apps.document"
};
const imageFile = Drive.Files.copy(
resource,
fileId,
{ ocr: true, ocrLanguage: "ja", supportsAllDrives: true }
);
const doc = DocumentApp.openById(imageFile.id);
const text = doc.getBody().getText();
try { Drive.Files.remove(imageFile.id); } catch (e) {}
return text;
}
ポイント:
supportsAllDrives: trueを指定することで共有ドライブのファイルにも対応- 処理後は一時ファイルを削除してドライブを汚さない
- Drive APIはサービスから v2 を選択する必要あり(v3ではOCR機能が利用できないため注意)
2. OCR結果の改行またぎ問題を正規表現で解決
PDFをOCRで読み取ると、表の1行が複数行に分断されることがあります。
OCR結果の例:
商品名(ライセンス名)
(example-dept.onmicrosoft.com)
(subscription id:xxxx-xxxx)
(有効期限 2026-02-01)
2 ← 数量
1,619円 ← 単価
3,238円 ← 金額
「1行ずつ処理する」方式では数量・単価・金額がバラバラになってしまいます。
そこで、テキスト全体に対して改行をまたぐ正規表現でパターンマッチさせることで解決しました。
// \s+ は改行にもマッチするのがポイント
const regexStandard =
/(\d{4}-\d{2}-\d{2})[\s\S]+?([\s\S]+?)\s+(\d+)\s+([\d,]+)円\s+([\d,]+)円/g;
const allMatches = [...text.matchAll(regexStandard)];
3. Masterシートで部署マスタを外出し管理
ドメインと部署名の対応をコードに直書きすると、部署が増えるたびにコード修正が必要になります。
Masterシートで管理することで、コードを触らずに対応できます。
| ドメイン / キーワード | 部署名 |
|---|---|
| example-dept-a.onmicrosoft.com | 〇〇部門A |
| example-dept-b.onmicrosoft.com | 〇〇部門B |
| example-keyword | 〇〇部門C |
function getDeptMap(sheet) {
const data = sheet.getRange(2, 1, sheet.getLastRow() - 1, 2).getValues();
let map = {};
data.forEach(row => { if (row[0] && row[1]) map[row[0]] = row[1]; });
return map;
}
4. 税込計算のズレを防ぐ設計
「行ごとに税込計算して合計する」と、端数処理の積み重ねでPDFの合計金額と数円ズレが生じます。
推移表の小計・総合計は 「税抜合計を先に出してから消費税をかける」方式で計算しています。
// 税抜合計を先に算出してから税込計算(四捨五入)
let sumTaxIn = Math.round(sumTaxExcl * CONFIG.TAX_RATE);
完成した推移表のイメージ
※部署名・ドメイン名はダミーデータです。
| 部署 / ドメイン | 2025-10 | 2025-11 | 2025-12 | 2026-01 |
|---|---|---|---|---|
| 〇〇部門A(税込) | 5,762 | 7,846 | 5,640 | 5,619 |
| example-dept-a... | 5,238 | 7,132 | 5,127 | 5,108 |
| 〇〇部門B(税込) | 14,823 | 72,460 | 287,830 | 177,773 |
| example-dept-b... | 13,475 | 65,873 | 261,664 | 161,612 |
| 総合計(税込) | 〇〇〇 | 〇〇〇 | 〇〇〇 | 〇〇〇 |
ハマったポイント
Drive APIのバージョン問題
GASのサービスからDrive APIを追加すると、デフォルトで v3 が選択されます。
OCR機能(Files.copy でのOCR)は v2 で動作するため、サービス設定でバージョンを v2 に変更する必要があります。
共有ドライブのファイルが見つからない問題
DriveApp.getFolderById() では共有ドライブのファイルを取得できないケースがありました。
Drive.Files.list() に supportsAllDrives: true と includeItemsFromAllDrives: true を指定することで解決しました。
const args = {
q: query,
supportsAllDrives: true,
includeItemsFromAllDrives: true,
maxResults: 50
};
files = Drive.Files.list(args);
まとめ
| 項目 | 内容 |
|---|---|
| 開発環境 | Google Apps Script |
| 使用API | Drive API v2(OCR) |
| 工数削減 | 毎月の転記・計算・投稿作業がボタン1クリックに |
| 保守性 | 部署追加はMasterシートに1行追記するだけ |
「PDFの自動読み取りなんて難しそう」 と思っていましたが、Drive APIのOCR機能を使えばGASだけで実現できました。
同じような月次請求管理の手作業に悩んでいる方の参考になれば幸いです。
参考リンク
- Google Apps Script 公式ドキュメント(https://developers.google.com/apps-script?hl=ja)
- Drive API v2 リファレンス(https://developers.google.com/workspace/drive/api/reference/rest/v2?hl=ja)







