Excel VBA初めて触った初心者がVBAでREST APIにPOSTする方法について書いてみた

Excel VBA (Visual Basic Application)でREST APIにPOSTする方法を書きます(2024/5/26時点情報)。本記事ではGoogleの生成AI GeminiのREST APIを利用しました。

# 検証した環境
- PC: XPS 13 9370
- OS: windows 11 (23H2 OS内部バージョン 22631.3593)。Excel for Macでは本記事に記載の内容だけではエラーが出て動かないのでご注意ください。
- Excel：Microsoft Office Home and Business 2016
# 環境設定
## 開発タブの有効化
Excel VBAを起動できるよう以下の手順でエクセルの「開発」タブを有効化します。

1. エクセルを起動
2. 左上の「ファイル」タブを押下
3. 左下の「オプション」を押下
4. ポップアップ画面の「リボンのユーザー設定」を押下
5. 「開発」にチェックを入れ（デフォルト設定ではチェックされていない）、右下の「OK」を押下
![リボンのユーザ設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/82b37fa8-8408-568a-2f7d-0265ec7cae1b.png)

これでエクセルに「開発タブ」が追加されます。
![開発タブ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/8c6d4e7d-0a7a-93cd-4828-619350fd9298.png)
## Gemini API KEYを取得
以下のGoogle AI for Developersのページから「Google AI StudioでAPIキーを取得する」を押下し、APIキーを発行します。
https://ai.google.dev/gemini-api

![屏幕截图 2024-05-26 171317.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/9f53c014-5a00-e37b-a886-83f8c76e6055.png)

作成後、以下のページで作成したAPIキーのプランが無料であることを念のため確認しておきましょう。
https://aistudio.google.com/app/apikey
![屏幕截图 2024-05-26 171710.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/bf474338-c48f-cfb8-751b-be7163e7809f.png)

利用料金については下記に記載があります。
https://ai.google.dev/pricing

# Gemini REST APIにPOSTする
## エクセルの準備
エクセルのシートを二つ作成します。
### Sheet1
プロンプトを記載しておくカラムと、生成結果を記入するカラムを作成し、右側にテキスト生成の実行ボタンとなる図形を設置しておきます。（図形は何でも構いません）
![屏幕截图 2024-05-26 180227.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/e7ec8dad-2775-b7a1-592a-cf12ace9bcfa.png)

プロンプトはなんでもいいですが、例として”Gemini 1.5 Flashの機能について100文字程度で教えてください。"にしておきます。

### Sheet2
二つ目のシートのB1セルに発行したGeminiのAPIKEYを記入します。
![屏幕截图 2024-05-26 175214.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/9976d887-4283-23cb-fd1e-9616c03352ca.png)

### ファイル保存
Excelマクロ有効ブック(.xlsm) の形式でファイルを保存します。
![屏幕截图 2024-05-26 183409.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/46591824-e5ff-970f-5a7c-0556e7025ad1.png)

## VBAの設定
### VBAの参照設定
Excel VBAでDictionary型を使用するためにMicrosoft Scripting Runtimeライブラリへの参照設定が必要のため、このライブラリを参照するよう設定します。
1. エクセルの「開発」タブを押下
1. 「Visual Basic」を押下し、VBAエディタを起動
1. VBAエディタの「ツール」タブの参照設定を押下
1. 参照可能なライブラリファイルの「Microsoft Scripting Runtime」にチェックを入れ、「OK」を押下
![屏幕截图 2024-05-25 181320.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/c86fe30b-3201-a6f5-a3cc-15e448195a04.png)
## JsonConverter.basのインポート
Jsonデータの処理を簡単に行えるよう、Json解析用のライブラリをVBAプロジェクトにインポートします。
1. JsonConverter.basのダウンロード：下記のリンクからJsonConverter.basのみダウンロードします
https://github.com/VBA-tools/VBA-JSON/blob/master/JsonConverter.bas
![jsonconverter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/65b25674-3313-4218-05a3-d82d80957e3e.png)
1. VBAエディタを開く: Excelの「開発」タブをクリックし、「Visual Basic」をクリックしてVBAエディタを開く。
2. JsonConverter.basをインポート:「ファイル」 > 「ファイルのインポート」を選択し、ダウンロードしたJsonConverter.basファイルを選択してインポートします。成功すれば下記のようにプロジェクト欄の標準モジュールにJsonConverterが表示されます。
![屏幕截图 2024-05-26 184051.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/b17f1707-6666-9f3b-eaeb-55234952df7d.png)

## Gemini REST APIへPOSTするVBAコード

以下のコードをVBAエディタに貼り付けます。
やっていることとしてはSheet1のA2セルのプロンプト文を取得し、Gemini 1.5 flashのREST APIにPOSTして結果を取得したあとにJsonを解析して、生成結果のみを取り出し、Sheet1のB2セルに書き込んでいます。

今回のサンプルコードではgemini-1.5-flashを利用しました。Geminiの各モデル詳細は下記に記載されています。
https://ai.google.dev/gemini-api/docs/models/gemini

```
' JsonConverterの初期設定
Const JsonLib As String = "JsonConverter"

Sub geminiMain()
    'gemini REST APIにPOSTし、結果を書き込むSubプロシージャ'
    Dim prompt As String
    Dim llmRes As String
    'sheet1のA2セルに記載のプロンプト文を取得'
    prompt = Worksheets("sheet1").Range("A2").Value
    'gemini REST APIにPOST'
    llmRes = geminiPost(prompt)
    'プロンプト文の右隣のセル（B2セル）に生成されたテキストを書き込み'
    ThisWorkbook.Sheets("Sheet1").Range("B2").Value = llmRes
    
End Sub

Function geminiPost(prompt As String)

    'gemini REST APIにPOSTするFunction'
    Dim url As String
    Dim requrl As String
    Dim http As Object
    Dim response As String
    Dim postData As String
    Dim jsonResponse As Object
    Dim parsedData As String
    Dim apiKey As String
    
    'APIKEY取得'
    apiKey = Worksheets("Sheet2").Range("B1").Value
    
    ' APIエンドポイントURLを設定
    url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key="
    requrl = url & apiKey
    ' 送信するデータ（例：JSON形式）
    postData = "{""contents"": [{""parts"":[{""text"": """ + prompt + """}]}]}"
    Debug.Print postData
    ' MSXML2.XMLHTTPオブジェクトを作成
    Set http = CreateObject("MSXML2.XMLHTTP")

    ' HTTPリクエストを開く
    http.Open "POST", requrl, False

    ' ヘッダーを追加
    http.setRequestHeader "Content-Type", "application/json"
    ' 追加のヘッダーが必要であればここに追加

    ' リクエストを送信
    http.Send postData

    ' レスポンスを取得
    response = http.responseText
    
    ' レスポンスの表示（デバッグ用）
    Debug.Print "Response: " & response
    
    ' JsonConverterを使ってレスポンスを解析
    Set jsonResponse = JsonConverter.ParseJson(response)
    
    ' 必要なデータを取り出す
    If Not jsonResponse Is Nothing Then
        If jsonResponse.Exists("candidates") Then
            parsedData = jsonResponse("candidates")(1)("content")("parts")(1)("text")
        Else
            parsedData = jsonResponse("error")("message")
        End If
    Else
        parsedData = "No valid JSON response"
    End If
    'Geminiの生成結果テキストもしくはエラーメッセージを返却"
    geminiPost = parsedData

End Function
```
VBAエディタに貼り付けると下記のような感じになります。
![屏幕截图 2024-05-26 184618.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/a0a19055-2877-8607-8c74-3f8e1e71e720.png)

### "必要なデータの取り出し"部分のコードについて
上記コードの「必要なデータを取り出す」の部分では、以下のAPIリファレンスを参考に、生成されたテキストのみを取り出し、parsedDataに格納しています。
- Geminiのコンテンツ生成のAPIリファレンス: 
https://ai.google.dev/api/rest/v1beta/models/generateContent
- レスポンス形式: https://ai.google.dev/api/rest/v1/GenerateContentResponse

Geminiのレスポンス例は以下です。
```
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "ここにGeminiが生成したテキストが格納される"
          }
        ],
        "role": "model"
      },
      "finishReason": "STOP",
      "index": 0,
      "safetyRatings": [
        {
          "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
          "probability": "NEGLIGIBLE"
        },
        {
          "category": "HARM_CATEGORY_HATE_SPEECH",
          "probability": "NEGLIGIBLE"
        },
        {
          "category": "HARM_CATEGORY_HARASSMENT",
          "probability": "NEGLIGIBLE"
        },
        {
          "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
          "probability": "NEGLIGIBLE"
        }
      ]
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 323,
    "candidatesTokenCount": 1,
    "totalTokenCount": 324
  }
}
```
VBAのコードではこの中で、"text"の値を取り出しています。注意点としてはVBAの場合はリストの1番目のインデックスが1なので
```
jsonResponse("candidates")(1)
```
のようにリストの1番目を取り出すときは1を指定します。
## 実行用ボタンにマクロを登録
エクセルで作成しておいたテキスト生成ボタンを右クリックし、「マクロの登録」を押下します。
![屏幕截图 2024-05-26 180618.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/1488a5ac-372f-745c-2105-8aac6ece78b3.png)

作成したSubプロシージャのgeminiMain()を選択します。
![屏幕截图 2024-05-26 180732.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/05ceafd1-66f8-9fb7-7ba1-f84c5852b17c.png)

## 実行
マクロを登録したボタンを押下することでVBAのgeminiMain()が実行され、Sheet1のB2セルに生成結果が書き込まれました！
![屏幕截图 2024-05-26 182413.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280368/b7da1d5a-1a4a-e4eb-ae7c-4203e5905795.png)

for文と組み合わせることでたくさんのプロンプトを一括で処理することも可能です。

# トラブルシューティング
## 1. jsonconverterの中のこのコード部分で”ユーザ定義型は定義されていません"というエラーがでる
Dictionary型を使用するためにMicrosoft Scripting Runtimeライブラリへの参照設定が必要。（プロジェクトごとに設定が必要）