Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

AIでPDF請求書をExcelに自動転記するAPI開発をやってみよう~その2~

Last updated at Posted at 2025-12-02

はじめに

こんにちは、フジマロです:grinning:

最近、生成AI技術の発展とともに、AIというワードを聞く頻度が多くなりました。
ひとことで表すとAIと呼ばれていますが、その中には機械学習、ディープラーニング、生成AIといった幅広い用語が登場します。

前回に引き続き今回は、ディープラーニング技術の代表的な例であるAIを使用したOCRサービスをAPIで試してみたいと思います。

前回のおさらい

Azure AI Document Itelligence事前構築済みモデルというサービスを使用してPDFの画像を文字に起こし、ファイルに書き込んでいく処理をAPIで作成します。
AzureADI.gif

前回は、 ➀画像ファイルをAIサービスにPOSTし画像IDを得る まで実施しました
今回は、 「➁画像IDを使いGETしたデータをExcelファイルに書込む」 をご紹介します

今回やること

➁画像IDを使いGETしたデータをExcelファイルに書込む
 11.画像IDからデータ取得
 12.ファイル書込み
 13.スキーマ
 14.mapper
 15.非同期処理
 16.実行と結果確認

AzureやHULFT Squareの設定について詳しくはこちらの前回の記事もご確認ください

➁画像IDを使いGETしたデータをExcelファイルに書込む

ここでは、アップロードした画像のIDから必要な文字データを取得する処理を作成します。
image.png

11.画像IDからデータ取得

HULFT Squareデザイナー画面の右側のツールパレットから下記の順番で設定します
[ネットワーク] - [REST] - [POST実行]

image.png

必須設定
名前:REST接続_AzureDocumentIntelligence ※「4.コネクションの設定」で設定したものを選択
パス:/documentintelligence/documentModels/prebuilt-invoice/analyzeResults/${ID}
クエリパラメーター:

名前
api-version 2024-11-30

レスポンス設定
データ形式: JSON ※選択

ヘッダ設定

リクエストヘッダ
名前
Ocp-Apim-Subscription-Key %{APIキー}

12.ファイル書込み

ここでは取得したデータを書き込むファイルを設定します。

image.png

必須設定
ファイル:/Personal/請求書.xlsx ※任意のファイルパス
列一覧:下記を設定

列名
請求会社名
日付
商品
金額

書き込み設定
[ 一行目に列名を挿入 ] にチェックを入れる

[完了]をクリック

13.スキーマ

ここではJson形式のデータをExcel形式に変換するための出力するJsonを定義します。
Web画面の方から下記の順番で作成していきます

[HULFT INTEGRATE] - [スキーマ] - [新規作成]

image.png

名前:AzureDocumentIntelligence請求書
タイプ:JSON ※選択
JSON定義:   ※下記を設定

請求書モデル(prebuilt-invoice)のレスポンスJson
Json
{
  "status": "succeeded",
  "createdDateTime": "2025-04-07T09:27:33Z",
  "lastUpdatedDateTime": "2025-04-07T09:27:36Z",
  "analyzeResult": {
    "apiVersion": "2024-02-29-preview",
    "modelId": "prebuilt-invoice",
    "stringIndexType": "textElements",
    "content": "請求書\n発行日 令和元年 8月31日\n〒102-0073",
    "pages": [
      {
        "pageNumber": 1,
        "angle": 0,
        "width": 792,
        "height": 1122,
        "unit": "pixel",
        "words": [
          {
            "content": "書",
            "polygon": [66],
            "confidence": 0.993,
            "span": {
              "offset": 2,
              "length": 1
            }
          }
        ],
        "lines": [
          {
            "content": "請求書",
            "polygon": [66],
            "spans": [
              {
                "offset": 0,
                "length": 3
              }
            ]
          }
        ],
        "spans": [
          {
            "offset": 0,
            "length": 483
          }
        ]
      }
    ],
    "tables": [
      {
        "rowCount": 2,
        "columnCount": 3,
        "cells": [
          {
            "kind": "columnHeader",
            "rowIndex": 0,
            "columnIndex": 0,
            "content": "今回御買上額",
            "boundingRegions": [
              {
                "pageNumber": 1,
                "polygon": [341]
              }
            ],
            "spans": [
              {
                "offset": 145,
                "length": 6
              }
            ]
          },
          {
            "rowIndex": 1,
            "columnIndex": 0,
            "content": "¥32,500",
            "boundingRegions": [
              {
                "pageNumber": 1,
                "polygon": [373]
              }
            ],
            "spans": [
              {
                "offset": 181,
                "length": 7
              }
            ]
          }
        ],
        "boundingRegions": [
          {
            "pageNumber": 1,
            "polygon": [375]
          }
        ],
        "spans": [
          {
            "offset": 145,
            "length": 18
          },
          {
            "offset": 181,
            "length": 22
          }
        ]
      },
      {
        "rowCount": 29,
        "columnCount": 8,
        "cells": [
          {
            "kind": "columnHeader",
            "rowIndex": 0,
            "columnIndex": 0,
            "content": "日付",
            "boundingRegions": [
              {
                "pageNumber": 1,
                "polygon": [398]
              }
            ],
            "spans": [
              {
                "offset": 204,
                "length": 2
              }
            ]
          },
          {
            "kind": "columnHeader",
            "rowIndex": 0,
            "columnIndex": 1,
            "content": "伝票No",
            "boundingRegions": [
              {
                "pageNumber": 1,
                "polygon": [398]
              }
            ],
            "spans": [
              {
                "offset": 207,
                "length": 4
              }
            ]
          },
          {
            "rowIndex": 1,
            "columnIndex": 0,
            "content": "8/10",
            "boundingRegions": [
              {
                "pageNumber": 1,
                "polygon": [70, 398, 106, 398, 106, 419, 70, 419]
              }
            ],
            "spans": [
              {
                "offset": 231,
                "length": 4
              }
            ]
          }
        ],
        "boundingRegions": [
          {
            "pageNumber": 1,
            "polygon": [1044]
          }
        ],
        "spans": [
          {
            "offset": 204,
            "length": 81
          }
        ]
      }
    ],
    "styles": [
      {
        "confidence": 0.4,
        "spans": [
          {
            "offset": 73,
            "length": 2
          }
        ],
        "isHandwritten": true
      }
    ],
    "documents": [
    {
        "docType": "invoice",
        "boundingRegions": [
            {
                "pageNumber": 1,
                "polygon": [1122]
            }
        ],
        "fields": {
            "CustomerAddress": {
                "type": "address",
                "content": "〒102-0073\n東京都千代田区九段北0-0-0",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [156]
                    }
                ],
                "confidence": 0.371,
                "spans": [
                    {
                        "offset": 19,
                        "length": 25
                    }
                ],
                "valueAddress": {
                    "houseNumber": "4-1-7",
                    "road": "九段北",
                    "postalCode": "〒102-0073",
                    "city": "東京都千代田区",
                    "streetAddress": "0-0-0 九段北"
                }
            },
            "CustomerAddressRecipient": {
                "type": "string",
                "valueString": "手作り弁当",
                "content": "手作り弁当",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [200]
                    }
                ],
                "confidence": 0.659,
                "spans": [
                    {
                        "offset": 57,
                        "length": 5
                    }
                ]
            },
            "CustomerName": {
                "type": "string",
                "valueString": "手作り弁当",
                "content": "手作り弁当",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [200]
                    }
                ],
                "confidence": 0.659,
                "spans": [
                    {
                        "offset": 57,
                        "length": 5
                    }
                ]
            },
            "InvoiceDate": {
                "type": "date",
                "valueDate": "2019-08-31",
                "content": "令和元年 8月31日",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [122]
                    }
                ],
                "confidence": 0.886,
                "spans": [
                    {
                        "offset": 8,
                        "length": 10
                    }
                ]
            },
            "InvoiceTotal": {
                "type": "currency",
                "valueCurrency": {
                    "currencySymbol": "¥",
                    "amount": 35100,
                    "currencyCode": "JPY"
                },
                "content": "¥35,100",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [368]
                    }
                ],
                "confidence": 0.954,
                "spans": [
                    {
                        "offset": 196,
                        "length": 7
                    }
                ]
            },
            "Items": {
                "type": "array",
                "valueArray": [
                    {
                        "type": "object",
                        "valueObject": {
                            "Amount": {
                                "type": "currency",
                                "valueCurrency": {
                                    "amount": 4000,
                                    "currencyCode": "JPY"
                                },
                                "content": "4,000",
                                "boundingRegions": [
                                    {
                                        "pageNumber": 1,
                                        "polygon": [418]
                                    }
                                ],
                                "confidence": 0.944,
                                "spans": [
                                    {
                                        "offset": 265,
                                        "length": 5
                                    }
                                ]
                            },
                            "Date": {
                                "type": "date",
                                "content": "8/10",
                                "boundingRegions": [
                                    {
                                        "pageNumber": 1,
                                        "polygon": [418]
                                    }
                                ],
                                "confidence": 0.918,
                                "spans": [
                                    {
                                        "offset": 231,
                                        "length": 4
                                    }
                                ]
                            },
                            "Description": {
                                "type": "string",
                                "valueString": "業務用しょうゆ 2L",
                                "content": "業務用しょうゆ 2L",
                                "boundingRegions": [
                                    {
                                        "pageNumber": 1,
                                        "polygon": [417]
                                    }
                                ],
                                "confidence": 0.94,
                                "spans": [
                                    {
                                        "offset": 245,
                                        "length": 10
                                    }
                                ]
                            },
                            "Quantity": {
                                "type": "number",
                                "valueNumber": 10,
                                "content": "10",
                                "boundingRegions": [
                                    {
                                        "pageNumber": 1,
                                        "polygon": [416]
                                    }
                                ],
                                "confidence": 0.944,
                                "spans": [
                                    {
                                        "offset": 256,
                                        "length": 2
                                    }
                                ]
                            },
                            "Unit": {
                                "type": "string",
                                "valueString": "本",
                                "content": "本",
                                "boundingRegions": [
                                    {
                                        "pageNumber": 1,
                                        "polygon": [417]
                                    }
                                ],
                                "confidence": 0.943,
                                "spans": [
                                    {
                                        "offset": 259,
                                        "length": 1
                                    }
                                ]
                            },
                            "UnitPrice": {
                                "type": "currency",
                                "valueCurrency": {
                                    "amount": 400,
                                    "currencyCode": "JPY"
                                },
                                "content": "400",
                                "boundingRegions": [
                                    {
                                        "pageNumber": 1,
                                        "polygon": [416]
                                    }
                                ],
                                "confidence": 0.944,
                                "spans": [
                                    {
                                        "offset": 261,
                                        "length": 3
                                    }
                                ]
                            }
                        },
                        "content": "8/10 60000100\n業務用しょうゆ 2L\n10\n\n400\n4,000",
                        "boundingRegions": [
                            {
                                "pageNumber": 1,
                                "polygon": [418]
                            }
                        ],
                        "confidence": 0.923,
                        "spans": [
                            {
                                "offset": 231,
                                "length": 39
                            }
                        ]
                    }
                ]
            },
            "SubTotal": {
                "type": "currency",
                "valueCurrency": {
                    "currencySymbol": "¥",
                    "amount": 32500,
                    "currencyCode": "JPY"
                },
                "content": "¥32,500",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [363]
                    }
                ],
                "confidence": 0.96,
                "spans": [
                    {
                        "offset": 181,
                        "length": 7
                    }
                ]
            },
            "TotalTax": {
                "type": "currency",
                "valueCurrency": {
                    "currencySymbol": "¥",
                    "amount": 2600,
                    "currencyCode": "JPY"
                },
                "content": "¥2,600",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [363]
                    }
                ],
                "confidence": 0.96,
                "spans": [
                    {
                        "offset": 189,
                        "length": 6
                    }
                ]
            },
            "VendorAddress": {
                "type": "address",
                "content": "〒151-8543\n東京都渋谷区本町1",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [225]
                    }
                ],
                "confidence": 0.536,
                "spans": [
                    {
                        "offset": 63,
                        "length": 9
                    }
                ],
                "valueAddress": {
                    "postalCode": "〒151-8543",
                    "city": "東",
                    "state": "京都渋谷",
                    "streetAddress": ""
                }
            },
            "VendorAddressRecipient": {
                "type": "string",
                "valueString": "食品株式会社",
                "content": "食品株式会社",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [261]
                    }
                ],
                "confidence": 0.698,
                "spans": [
                    {
                        "offset": 112,
                        "length": 8
                    }
                ]
            },
            "VendorName": {
                "type": "string",
                "valueString": "食\nサラダ\nE",
                "content": "食\nサラダ\nE",
                "boundingRegions": [
                    {
                        "pageNumber": 1,
                        "polygon": [281]
                    }
                ],
                "confidence": 0.306,
                "spans": [
                    {
                        "offset": 73,
                        "length": 2
                    }
                ]
            }
        },
        "confidence": 1,
        "spans": [
            {
                "offset": 0,
                "length": 483
					}
				]
			}
		],
		"contentFormat": "text"
	}
}

14.mapper

ここではAIから受け取ったJSONデータをExcel形式に変換するためのmapperを設定していきます

デザイナー画面に戻り下記のように設定します。

➀アイコン [execute_http_get] → [excel_write] をドラッグ&ドロップする  
➁下記を選択する
  [プロセスフローとデータフローを引く]
   [マッピングを追加する]

設定したら OK を押下
image.png

mappingアイコンが配置されたらダブルクリックで開きます
image.png

execute_http_get を右クリックしてリソースからスキーマを読み込む を押下
image.png

先程13.スキーマ で作成した AzureDocumentIntelligence請求書を選択します。
すると [入力データ] にJsonが定義されます。

下記の項目を繋いでいきます

親オブジェクト  入力データ 出力データ
CustomerName content 請求会社名
InvoiceDate valueDate 日付
valueObject content 商品
UnitPrice content 金額

image.png

image.png

image.png

image.png

次に
Jsonの中で表形式の箇所を繰り返し入力します。
画面右のツールパレットの中から下記のロジックをドラッグ&ドロップで中央に配置します。
[繰り返し] - [基本] - [単純な繰り返し]

image.png

ポップアップが開きますが [完了] を押下してください
[単純な繰り返し]アイコンが配置されたら下記のように繋いでいきます

image.png

親オブジェクト 入力データ ロジック 出力データ
Items element [単純な繰り返し] row

ここまでで一通りのデータの紐づけが完了しました。

画像に含まれるAI OCR出力結果のノイズをキレイにしよう
文字におこしたいPDFファイルには画像に予期しない文字が含まれていることはよくあると思います。
このようなノイズを除去する方法もご紹介します

image.png

正規表現置換ロジックを配置してノイズを除去する処理を作成します。
画面右のツールパレットの中から下記のロジックをドラッグ&ドロップで中央に配置します。
[文字列] - [変換] - [正規表現置換]

正規表現置換ロジックに下記を入力します

置換前文字列(正規表現パターン) [^\w\sぁ-んァ-ン一-龥]+
置換後文字列  

image.png

※補足

正規表現 意味
\w 英数字、アンダースコア
\s 空白
ぁ-ん 平仮名
ァ-ン カタカナ
一-龥 漢字
[^...] それ以外の文字にマッチ(除去対象)
+ 連続する対象文字列にマッチ

配置した 正規表現置換 ロジックを下記のようにノイズを除去したい項目に繋いでいきます

image.png

親オブジェクト 入力データ ロジック 出力データ
UnitPrice content [正規表現置換ロジック] 金額

これでノイズを除去することが出来ます

15.非同期処理

ここではAIの処理が完了したか、確認するための同期をとる仕組みを作成していきます

Document IntelligencのAPI仕様で行われる一連の処理は下記になります

➀画像をPOSTしてAIに読み込ませる(未完了でも画像IDは出力される)
➁AIが文字に起こす
➂処理が完了
➃データとして出力

上記の➂はGETしたデータのstatusの項目から確認することが出来ます

image.png

新しくステータスを取得する変数 status を作成します
image.png

mapperの入力データのJson項目に status がありますので、変数 status にドラッグ&ドロップで繋ぎます
完了したらmapperを閉じてください
image.png

次に画像IDが出力されてから待機する処理を作成します。
画面右側のツールパレットから下記の順番でアイコンを配置します。
[基本] - [処理] - [待機]
ポップアップが出てきますので 10秒 と入力します
image.png

次に画像IDが出力されたあと、10秒待機してからGETを繰り返す処理を作成します。
画面右側のツールパレットから下記の順番でアイコンを配置します。
[基本] - [フロー] - [繰り返し(条件指定)]

image.png

繰り返しロジックに下記のように設定しましょう

変数 [status] が [次の値と等しくない間] [succeeded]

image.png
設定したら 完了 を押下してください

アイコンが配置できましたらドラッグ&ドロップで下記のように線で繋いでください
image.png

こうすることでGETした値が未完了を示していたら繰り返し、完了していたらファイルに書込みます

16.実行と結果確認

ここまで作成できたら実行します。
画面右上の デバック実行 ボタンを押下してください
image.png

処理が完了したことを確認してください
image.png

実行ログを見ると正常にデータが書き込まれたことを確認できます
image.png

出力結果を見ると無事にデータが書き込まれていることが確認できました
image.png

もとのPDFファイルの内容は下記になります
image.png

最後に

いかがでしたでしょうか
ここまで読んで頂きありがとうございます
今回は、事前にトレーニングされているモデルを使用しましたが、現在のAIの精度ではノイズが多く
自分で画像をトレーニングさせてもAIが出力するデータにはノイズが含まれることはよくあります。
そのためノーコードで実装できたうえにロジックでノイズを除去できる方法があるのはかなり嬉しい機能でした

この記事の内容がみなさんの参考になれば「いいね」をおして頂けると嬉しいです。

ここまで読んでいただきありがとうございました。それでは、また!:raised_hand:

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?