<h1 class="wp-block-heading">VBAでREST APIリクエスト: 外部ライブラリ不使用の高度な連携</h1>
<p>VBAによるREST APIリクエストは、外部ライブラリ不使用でHTTP通信を実現し、Officeアプリケーションのデータ連携能力を拡張する。</p>
<h2 class="wp-block-heading">背景/要件</h2>
<p>企業システムにおいて、ExcelやAccessから外部Webサービスと連携し、データ取得や更新を行う需要は高い。しかし、セキュリティポリシーにより追加のCOMコンポーネントやDLLのインストールが制限される環境では、一般的なREST APIクライアントライブラリの使用が困難である。この要件に対し、Windows OSに標準搭載されている<code>WinHttpRequest</code>オブジェクト(<code>WinHttp.dll</code>経由で提供されるCOMコンポーネント)を利用し、外部ライブラリへの依存なしにREST APIリクエストを実現する。さらに、Win32 APIを直接利用した性能測定や、Officeアプリケーション特有の性能チューニングを適用する。</p>
<h2 class="wp-block-heading">設計</h2>
<p>REST APIリクエストの処理は、<code>WinHttpRequest</code>オブジェクトを中心に構築する。リクエストの準備、送信、レスポンスの受信、およびその後のデータ処理が主要なフェーズとなる。JSON形式のレスポンスは、VBAの文字列操作や正規表現オブジェクト(<code>VBScript.RegExp</code>)を用いて解析する。</p>
<h3 class="wp-block-heading">データモデル</h3>
<ul class="wp-block-list">
<li><strong>リクエスト</strong>: URL (String), メソッド (String: GET, POST, PUT, DELETE), ヘッダー (Dictionary/Collection), ボディ (String)。</li>
<li><strong>レスポンス</strong>: ステータスコード (Long), ステータステキスト (String), ヘッダー (String), ボディ (String)。</li>
</ul>
<h3 class="wp-block-heading">処理フロー</h3>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["VBAマクロ開始"] --> B{"APIリクエスト準備"};
B --> C["WinHttpRequestオブジェクト作成"];
C --> D["HTTPメソッド/URL設定 (GET/POST)"];
D --> E["ヘッダー設定 (Content-Type, Authorization)"];
E --> F["リクエストボディ設定 (POSTの場合)"];
F --> G["Sendリクエスト実行"];
G --> H{"レスポンス待機"};
H -- 成功 --> I{"HTTPステータスコード判定"};
I -- 200 OK --> J["レスポンスボディ取得"];
J --> K["JSONパース/データ抽出"];
K --> L["データ整形/加工"];
L --> M["Officeアプリへの書き込み (Excelシート/Accessテーブル)"];
M --> N["VBAマクロ終了"];
I -- エラー (4xx/5xx) --> O["エラーハンドリング/ログ記録"];
O --> N;
H -- タイムアウト/ネットワークエラー --> O;
</pre></div>
<h2 class="wp-block-heading">実装</h2>
<p>以下のコードは、ExcelおよびAccessを対象に、<code>WinHttpRequest</code>とWin32 API、VBA組み込み関数を組み合わせてREST APIリクエストを実行する。JSONPlaceholderという公開APIを利用してデータの取得と更新をシミュレートする。</p>
<h3 class="wp-block-heading">性能計測のためのWin32 API宣言</h3>
<pre data-enlighter-language="generic">' 標準モジュールに記述
#If VBA7 Then
Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
#Else
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
#End If
Private m_freq As Currency ' 1秒間のカウント数
Private m_start As Currency ' 計測開始時のカウント
Public Sub StartTimer()
QueryPerformanceFrequency m_freq
QueryPerformanceCounter m_start
End Sub
Public Function StopTimer() As Double
Dim stopTime As Currency
QueryPerformanceCounter stopTime
StopTimer = (stopTime - m_start) / m_freq
End Function
' VBScript.RegExpオブジェクトの参照設定なし利用
Public Function GetRegExpObject() As Object
On Error Resume Next
Set GetRegExpObject = CreateObject("VBScript.RegExp")
On Error GoTo 0
End Function
</pre>
<h3 class="wp-block-heading">Excelでの実装: GETリクエストとシートへの書き込み</h3>
<p>JSONPlaceholderからTODOリストを取得し、Excelシートに書き込む。</p>
<pre data-enlighter-language="generic">' 標準モジュールに記述
Public Sub GetApiDataToExcel()
Dim httpReq As Object
Dim apiUrl As String
Dim jsonResponse As String
Dim regEx As Object ' VBScript.RegExp
Dim matches As Object
Dim match As Object
Dim ws As Worksheet
Dim lastRow As Long
Dim data() As Variant
Dim i As Long
Dim currentDataRow As Long
' 性能計測開始
StartTimer
' Excel最適化設定
Set ws = ThisWorkbook.Sheets("Sheet1")
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
apiUrl = "https://jsonplaceholder.typicode.com/todos"
' WinHttpRequestオブジェクトの作成
Set httpReq = CreateObject("WinHttp.WinHttpRequest.5.1")
With httpReq
.Open "GET", apiUrl, False ' 同期リクエスト
.Send
If .Status = 200 Then
jsonResponse = .ResponseText
Else
MsgBox "APIエラー: " & .Status & " - " & .StatusText, vbCritical
GoTo CleanUp
End If
End With
' JSON簡易パース (正規表現を使用)
Set regEx = GetRegExpObject
If regEx Is Nothing Then
MsgBox "VBScript.RegExpオブジェクトを作成できません。", vbCritical
GoTo CleanUp
End If
With regEx
.Pattern = """id"": (\d+),\s*""title"": ""([^""]+)"",\s*""completed"": (true|false)"
.Global = True
Set matches = .Execute(jsonResponse)
End With
If matches.Count = 0 Then
MsgBox "データが見つかりませんでした。", vbInformation
GoTo CleanUp
End If
' 配列にデータを格納
ReDim data(1 To matches.Count, 1 To 3)
currentDataRow = 1
For Each match In matches
data(currentDataRow, 1) = CLng(match.SubMatches(0)) ' id
data(currentDataRow, 2) = match.SubMatches(1) ' title
data(currentDataRow, 3) = CBool(match.SubMatches(2)) ' completed
currentDataRow = currentDataRow + 1
Next match
' シートにヘッダーを書き込み
ws.Cells.ClearContents
ws.Range("A1").Value = "ID"
ws.Range("B1").Value = "Title"
ws.Range("C1").Value = "Completed"
ws.Range("A1:C1").Font.Bold = True
' 配列からシートに一括書き込み
If currentDataRow > 1 Then
ws.Range("A2").Resize(UBound(data, 1), UBound(data, 2)).Value = data
End If
ws.Columns("A:C").AutoFit
MsgBox "APIデータが正常に取得され、シートに書き込まれました。" & vbCrLf & _
"処理時間: " & Format(StopTimer, "0.000") & "秒", vbInformation
CleanUp:
Set httpReq = Nothing
Set regEx = Nothing
Set matches = Nothing
Set ws = Nothing
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
End Sub
</pre>
<h3 class="wp-block-heading">Accessでの実装: POSTリクエストとテーブルへの挿入</h3>
<p>JSONPlaceholderに新しいTODOアイテムを送信し、その結果をAccessテーブルに保存する。</p>
<pre data-enlighter-language="generic">' 標準モジュールに記述
Public Sub PostApiDataToAccess()
Dim httpReq As Object
Dim apiUrl As String
Dim requestBody As String
Dim jsonResponse As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim query As String
Dim userId As Long, id As Long
Dim title As String, completed As Boolean
' 性能計測開始
StartTimer
' テーブルが存在しない場合は作成
Set db = CurrentDb
On Error Resume Next
Set tdf = db.TableDefs("ApiTodoResults")
On Error GoTo 0
If tdf Is Nothing Then
Set tdf = db.CreateTableDef("ApiTodoResults")
With tdf
Set fld = .CreateField("UserID", dbLong)
.Fields.Append fld
Set fld = .CreateField("ID", dbLong)
.Fields.Append fld
Set fld = .CreateField("Title", dbText, 255)
.Fields.Append fld
Set fld = .CreateField("Completed", dbBoolean)
.Fields.Append fld
End With
db.TableDefs.Append tdf
Set tdf = Nothing
MsgBox "テーブル 'ApiTodoResults' が作成されました。", vbInformation
End If
apiUrl = "https://jsonplaceholder.typicode.com/todos"
userId = 1
title = "Learn VBA REST API"
completed = False
requestBody = "{""userId"": " & userId & ", ""title"": """ & title & """, ""completed"": " & LCase(CStr(completed)) & "}"
' WinHttpRequestオブジェクトの作成
Set httpReq = CreateObject("WinHttp.WinHttpRequest.5.1")
With httpReq
.Open "POST", apiUrl, False ' 同期リクエスト
.SetRequestHeader "Content-Type", "application/json; charset=UTF-8"
.Send requestBody
If .Status = 201 Then ' Created
jsonResponse = .ResponseText
Else
MsgBox "APIエラー: " & .Status & " - " & .StatusText, vbCritical
GoTo CleanUp
End If
End With
' JSON簡易パース (JSONPlaceholderのPOSTレスポンスは新しく作成されたオブジェクト)
' 例: {"userId": 1, "title": "...", "completed": false, "id": 201}
Dim regEx As Object
Set regEx = GetRegExpObject
If regEx Is Nothing Then
MsgBox "VBScript.RegExpオブジェクトを作成できません。", vbCritical
GoTo CleanUp
End If
' idの抽出
regEx.Pattern = """id"": (\d+)"
Set tdf = regEx.Execute(jsonResponse)
If tdf.Count > 0 Then id = CLng(tdf(0).SubMatches(0)) Else id = 0
' Accessテーブルに結果を挿入 (DAO使用)
db.BeginTrans ' トランザクション開始
Set rs = db.OpenRecordset("ApiTodoResults", dbOpenDynaset)
With rs
.AddNew
.Fields("UserID").Value = userId
.Fields("ID").Value = id
.Fields("Title").Value = title
.Fields("Completed").Value = completed
.Update
End With
db.CommitTrans ' トランザクションコミット
MsgBox "APIにデータが送信され、Accessテーブルに結果が保存されました。" & vbCrLf & _
"処理時間: " & Format(StopTimer, "0.000") & "秒", vbInformation
CleanUp:
On Error Resume Next
If Not rs Is Nothing Then
If rs.EditMode = dbEditAdd Then rs.CancelUpdate ' エラーでAddnew中の場合
rs.Close
End If
If db.Transactions Then db.Rollback ' エラーでトランザクションが残っている場合
Set rs = Nothing
Set db = Nothing
Set httpReq = Nothing
Set regEx = Nothing
On Error GoTo 0
End Sub
</pre>
<h2 class="wp-block-heading">検証</h2>
<ol class="wp-block-list">
<li><strong>GETリクエスト</strong>: <code>GetApiDataToExcel</code> を実行し、Sheet1にJSONPlaceholderのTODOリストが正しくID、Title、Completedの列で表示されることを確認する。HTTPステータスが200であることをVBAの実行ログやメッセージボックスで確認する。</li>
<li><strong>POSTリクエスト</strong>: <code>PostApiDataToAccess</code> を実行し、Accessの「ApiTodoResults」テーブルに新しいエントリが追加されることを確認する。HTTPステータスが201であることを確認する。</li>
<li><strong>エラーハンドリング</strong>: ネットワークを切断してコードを実行し、APIエラーメッセージが正しく表示されることを確認する。</li>
<li><strong>性能チューニング</strong>: <code>GetApiDataToExcel</code> の実行時間を、<code>Application.ScreenUpdating</code> などを無効化した場合と有効な場合で比較する。例えば、1000件のデータ取得の場合、無効化することで約10秒から0.5秒以下に短縮されるといった差が確認できる。<code>PostApiDataToAccess</code>では、トランザクションを使用しない場合と比較し、大量データ挿入時の速度向上(例: 1000件で1秒から0.1秒へ)を確認する。</li>
</ol>
<h2 class="wp-block-heading">運用</h2>
<h3 class="wp-block-heading">実行手順</h3>
<ol class="wp-block-list">
<li>上記VBAコードをExcelまたはAccessの標準モジュールにコピーする。</li>
<li>Excelの場合は、シート名を「Sheet1」とするか、コード内のシート名を適宜変更する。</li>
<li>マクロを有効化する(ファイル>オプション>トラストセンター>トラストセンターの設定>マクロの設定)。</li>
<li>VBAエディタ(Alt+F11)から該当プロシージャを実行するか、ボタンなどにマクロを割り当てて実行する。</li>
</ol>
<h3 class="wp-block-heading">ロールバック方法</h3>
<p>APIリクエストの実行後に問題が発生した場合、以下の手順でロールバック可能である。</p>
<ol class="wp-block-list">
<li><strong>Excel</strong>: シートのデータは上書きされるため、実行前のデータを手動でバックアップするか、ファイルを閉じる際に変更を保存しない。</li>
<li><strong>Access</strong>: <code>PostApiDataToAccess</code> プロシージャはトランザクションを使用しているため、データ挿入中にエラーが発生した場合は自動的にロールバックされる。正常に完了したデータについては、Accessのテーブルから対象レコードを手動で削除する。</li>
<li><strong>VBAコード</strong>: 異常動作が確認された場合は、VBAモジュールからコードを削除するか、コメントアウトして無効化する。</li>
</ol>
<h3 class="wp-block-heading">認証情報の管理</h3>
<p>APIキーや認証トークンはコードに直書きせず、以下の方法で管理する。</p>
<ul class="wp-block-list">
<li><strong>Excel</strong>: 特定のシートのセル、または非表示の名前付き範囲に格納する。</li>
<li><strong>Access</strong>: 別のテーブルに格納し、必要な時に取得する。</li>
<li><strong>環境変数</strong>: Windowsの環境変数に設定し、VBAから<code>Environ()</code>関数で取得する。</li>
</ul>
<h2 class="wp-block-heading">落とし穴</h2>
<ul class="wp-block-list">
<li><strong>複雑なJSONパース</strong>: 外部ライブラリがない場合、ネストが深いJSONや動的なキーを持つJSONのパースは正規表現だけでは非常に困難になる。簡易的なJSON構造に限定するか、自前でパースロジックを実装する必要がある。</li>
<li><strong>非同期リクエスト</strong>: <code>WinHttpRequest</code>は非同期モードもサポートするが、VBAではイベントハンドリングが煩雑なため、実務では同期リクエストが一般的である。長時間の処理にはUIフリーズを避ける工夫が必要。</li>
<li><strong>文字コード</strong>: <code>WinHttpRequest</code>は<code>ResponseText</code>プロパティでUTF-8などを自動的に処理するが、まれに文字化けが発生する場合がある。その際は<code>ResponseBody</code>(バイト配列)を取得し、<code>ADODB.Stream</code>オブジェクトなどで明示的に変換を試みる必要がある。</li>
<li><strong>タイムアウト</strong>: ネットワーク状況によりAPIリクエストが長時間応答しない場合があるため、<code>WinHttpRequest.SetTimeouts</code>メソッドで明示的にタイムアウトを設定することが重要である。</li>
<li><strong>セキュリティ</strong>: APIキーなどの認証情報をコード内やファイル内に保存する際は、アクセス制限や暗号化などの対策を講じる必要がある。</li>
</ul>
<h2 class="wp-block-heading">まとめ</h2>
<p>VBAにおけるREST APIリクエストは、<code>WinHttpRequest</code>オブジェクトと<code>VBScript.RegExp</code>、そしてWin32 APIを組み合わせることで、外部ライブラリに依存せず実現可能である。ExcelやAccessといったOfficeアプリケーションの性能特性を理解し、<code>ScreenUpdating</code>の無効化、配列への一括書き込み、データベーストランザクションといった最適化手法を適用することで、実用的な処理速度を確保できる。これにより、Office環境から外部Webサービスと連携し、業務の自動化とデータ活用の幅を広げられる。</p>
VBAでREST APIリクエスト: 外部ライブラリ不使用の高度な連携
VBAによるREST APIリクエストは、外部ライブラリ不使用でHTTP通信を実現し、Officeアプリケーションのデータ連携能力を拡張する。
背景/要件
企業システムにおいて、ExcelやAccessから外部Webサービスと連携し、データ取得や更新を行う需要は高い。しかし、セキュリティポリシーにより追加のCOMコンポーネントやDLLのインストールが制限される環境では、一般的なREST APIクライアントライブラリの使用が困難である。この要件に対し、Windows OSに標準搭載されているWinHttpRequest
オブジェクト(WinHttp.dll
経由で提供されるCOMコンポーネント)を利用し、外部ライブラリへの依存なしにREST APIリクエストを実現する。さらに、Win32 APIを直接利用した性能測定や、Officeアプリケーション特有の性能チューニングを適用する。
設計
REST APIリクエストの処理は、WinHttpRequest
オブジェクトを中心に構築する。リクエストの準備、送信、レスポンスの受信、およびその後のデータ処理が主要なフェーズとなる。JSON形式のレスポンスは、VBAの文字列操作や正規表現オブジェクト(VBScript.RegExp
)を用いて解析する。
データモデル
- リクエスト: URL (String), メソッド (String: GET, POST, PUT, DELETE), ヘッダー (Dictionary/Collection), ボディ (String)。
- レスポンス: ステータスコード (Long), ステータステキスト (String), ヘッダー (String), ボディ (String)。
処理フロー
graph TD
A["VBAマクロ開始"] --> B{"APIリクエスト準備"};
B --> C["WinHttpRequestオブジェクト作成"];
C --> D["HTTPメソッド/URL設定 (GET/POST)"];
D --> E["ヘッダー設定 (Content-Type, Authorization)"];
E --> F["リクエストボディ設定 (POSTの場合)"];
F --> G["Sendリクエスト実行"];
G --> H{"レスポンス待機"};
H -- 成功 --> I{"HTTPステータスコード判定"};
I -- 200 OK --> J["レスポンスボディ取得"];
J --> K["JSONパース/データ抽出"];
K --> L["データ整形/加工"];
L --> M["Officeアプリへの書き込み (Excelシート/Accessテーブル)"];
M --> N["VBAマクロ終了"];
I -- エラー (4xx/5xx) --> O["エラーハンドリング/ログ記録"];
O --> N;
H -- タイムアウト/ネットワークエラー --> O;
実装
以下のコードは、ExcelおよびAccessを対象に、WinHttpRequest
とWin32 API、VBA組み込み関数を組み合わせてREST APIリクエストを実行する。JSONPlaceholderという公開APIを利用してデータの取得と更新をシミュレートする。
性能計測のためのWin32 API宣言
' 標準モジュールに記述
#If VBA7 Then
Private Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
#Else
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
#End If
Private m_freq As Currency ' 1秒間のカウント数
Private m_start As Currency ' 計測開始時のカウント
Public Sub StartTimer()
QueryPerformanceFrequency m_freq
QueryPerformanceCounter m_start
End Sub
Public Function StopTimer() As Double
Dim stopTime As Currency
QueryPerformanceCounter stopTime
StopTimer = (stopTime - m_start) / m_freq
End Function
' VBScript.RegExpオブジェクトの参照設定なし利用
Public Function GetRegExpObject() As Object
On Error Resume Next
Set GetRegExpObject = CreateObject("VBScript.RegExp")
On Error GoTo 0
End Function
Excelでの実装: GETリクエストとシートへの書き込み
JSONPlaceholderからTODOリストを取得し、Excelシートに書き込む。
' 標準モジュールに記述
Public Sub GetApiDataToExcel()
Dim httpReq As Object
Dim apiUrl As String
Dim jsonResponse As String
Dim regEx As Object ' VBScript.RegExp
Dim matches As Object
Dim match As Object
Dim ws As Worksheet
Dim lastRow As Long
Dim data() As Variant
Dim i As Long
Dim currentDataRow As Long
' 性能計測開始
StartTimer
' Excel最適化設定
Set ws = ThisWorkbook.Sheets("Sheet1")
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
apiUrl = "https://jsonplaceholder.typicode.com/todos"
' WinHttpRequestオブジェクトの作成
Set httpReq = CreateObject("WinHttp.WinHttpRequest.5.1")
With httpReq
.Open "GET", apiUrl, False ' 同期リクエスト
.Send
If .Status = 200 Then
jsonResponse = .ResponseText
Else
MsgBox "APIエラー: " & .Status & " - " & .StatusText, vbCritical
GoTo CleanUp
End If
End With
' JSON簡易パース (正規表現を使用)
Set regEx = GetRegExpObject
If regEx Is Nothing Then
MsgBox "VBScript.RegExpオブジェクトを作成できません。", vbCritical
GoTo CleanUp
End If
With regEx
.Pattern = """id"": (\d+),\s*""title"": ""([^""]+)"",\s*""completed"": (true|false)"
.Global = True
Set matches = .Execute(jsonResponse)
End With
If matches.Count = 0 Then
MsgBox "データが見つかりませんでした。", vbInformation
GoTo CleanUp
End If
' 配列にデータを格納
ReDim data(1 To matches.Count, 1 To 3)
currentDataRow = 1
For Each match In matches
data(currentDataRow, 1) = CLng(match.SubMatches(0)) ' id
data(currentDataRow, 2) = match.SubMatches(1) ' title
data(currentDataRow, 3) = CBool(match.SubMatches(2)) ' completed
currentDataRow = currentDataRow + 1
Next match
' シートにヘッダーを書き込み
ws.Cells.ClearContents
ws.Range("A1").Value = "ID"
ws.Range("B1").Value = "Title"
ws.Range("C1").Value = "Completed"
ws.Range("A1:C1").Font.Bold = True
' 配列からシートに一括書き込み
If currentDataRow > 1 Then
ws.Range("A2").Resize(UBound(data, 1), UBound(data, 2)).Value = data
End If
ws.Columns("A:C").AutoFit
MsgBox "APIデータが正常に取得され、シートに書き込まれました。" & vbCrLf & _
"処理時間: " & Format(StopTimer, "0.000") & "秒", vbInformation
CleanUp:
Set httpReq = Nothing
Set regEx = Nothing
Set matches = Nothing
Set ws = Nothing
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
End Sub
Accessでの実装: POSTリクエストとテーブルへの挿入
JSONPlaceholderに新しいTODOアイテムを送信し、その結果をAccessテーブルに保存する。
' 標準モジュールに記述
Public Sub PostApiDataToAccess()
Dim httpReq As Object
Dim apiUrl As String
Dim requestBody As String
Dim jsonResponse As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim query As String
Dim userId As Long, id As Long
Dim title As String, completed As Boolean
' 性能計測開始
StartTimer
' テーブルが存在しない場合は作成
Set db = CurrentDb
On Error Resume Next
Set tdf = db.TableDefs("ApiTodoResults")
On Error GoTo 0
If tdf Is Nothing Then
Set tdf = db.CreateTableDef("ApiTodoResults")
With tdf
Set fld = .CreateField("UserID", dbLong)
.Fields.Append fld
Set fld = .CreateField("ID", dbLong)
.Fields.Append fld
Set fld = .CreateField("Title", dbText, 255)
.Fields.Append fld
Set fld = .CreateField("Completed", dbBoolean)
.Fields.Append fld
End With
db.TableDefs.Append tdf
Set tdf = Nothing
MsgBox "テーブル 'ApiTodoResults' が作成されました。", vbInformation
End If
apiUrl = "https://jsonplaceholder.typicode.com/todos"
userId = 1
title = "Learn VBA REST API"
completed = False
requestBody = "{""userId"": " & userId & ", ""title"": """ & title & """, ""completed"": " & LCase(CStr(completed)) & "}"
' WinHttpRequestオブジェクトの作成
Set httpReq = CreateObject("WinHttp.WinHttpRequest.5.1")
With httpReq
.Open "POST", apiUrl, False ' 同期リクエスト
.SetRequestHeader "Content-Type", "application/json; charset=UTF-8"
.Send requestBody
If .Status = 201 Then ' Created
jsonResponse = .ResponseText
Else
MsgBox "APIエラー: " & .Status & " - " & .StatusText, vbCritical
GoTo CleanUp
End If
End With
' JSON簡易パース (JSONPlaceholderのPOSTレスポンスは新しく作成されたオブジェクト)
' 例: {"userId": 1, "title": "...", "completed": false, "id": 201}
Dim regEx As Object
Set regEx = GetRegExpObject
If regEx Is Nothing Then
MsgBox "VBScript.RegExpオブジェクトを作成できません。", vbCritical
GoTo CleanUp
End If
' idの抽出
regEx.Pattern = """id"": (\d+)"
Set tdf = regEx.Execute(jsonResponse)
If tdf.Count > 0 Then id = CLng(tdf(0).SubMatches(0)) Else id = 0
' Accessテーブルに結果を挿入 (DAO使用)
db.BeginTrans ' トランザクション開始
Set rs = db.OpenRecordset("ApiTodoResults", dbOpenDynaset)
With rs
.AddNew
.Fields("UserID").Value = userId
.Fields("ID").Value = id
.Fields("Title").Value = title
.Fields("Completed").Value = completed
.Update
End With
db.CommitTrans ' トランザクションコミット
MsgBox "APIにデータが送信され、Accessテーブルに結果が保存されました。" & vbCrLf & _
"処理時間: " & Format(StopTimer, "0.000") & "秒", vbInformation
CleanUp:
On Error Resume Next
If Not rs Is Nothing Then
If rs.EditMode = dbEditAdd Then rs.CancelUpdate ' エラーでAddnew中の場合
rs.Close
End If
If db.Transactions Then db.Rollback ' エラーでトランザクションが残っている場合
Set rs = Nothing
Set db = Nothing
Set httpReq = Nothing
Set regEx = Nothing
On Error GoTo 0
End Sub
検証
- GETリクエスト:
GetApiDataToExcel
を実行し、Sheet1にJSONPlaceholderのTODOリストが正しくID、Title、Completedの列で表示されることを確認する。HTTPステータスが200であることをVBAの実行ログやメッセージボックスで確認する。
- POSTリクエスト:
PostApiDataToAccess
を実行し、Accessの「ApiTodoResults」テーブルに新しいエントリが追加されることを確認する。HTTPステータスが201であることを確認する。
- エラーハンドリング: ネットワークを切断してコードを実行し、APIエラーメッセージが正しく表示されることを確認する。
- 性能チューニング:
GetApiDataToExcel
の実行時間を、Application.ScreenUpdating
などを無効化した場合と有効な場合で比較する。例えば、1000件のデータ取得の場合、無効化することで約10秒から0.5秒以下に短縮されるといった差が確認できる。PostApiDataToAccess
では、トランザクションを使用しない場合と比較し、大量データ挿入時の速度向上(例: 1000件で1秒から0.1秒へ)を確認する。
運用
実行手順
- 上記VBAコードをExcelまたはAccessの標準モジュールにコピーする。
- Excelの場合は、シート名を「Sheet1」とするか、コード内のシート名を適宜変更する。
- マクロを有効化する(ファイル>オプション>トラストセンター>トラストセンターの設定>マクロの設定)。
- VBAエディタ(Alt+F11)から該当プロシージャを実行するか、ボタンなどにマクロを割り当てて実行する。
ロールバック方法
APIリクエストの実行後に問題が発生した場合、以下の手順でロールバック可能である。
- Excel: シートのデータは上書きされるため、実行前のデータを手動でバックアップするか、ファイルを閉じる際に変更を保存しない。
- Access:
PostApiDataToAccess
プロシージャはトランザクションを使用しているため、データ挿入中にエラーが発生した場合は自動的にロールバックされる。正常に完了したデータについては、Accessのテーブルから対象レコードを手動で削除する。
- VBAコード: 異常動作が確認された場合は、VBAモジュールからコードを削除するか、コメントアウトして無効化する。
認証情報の管理
APIキーや認証トークンはコードに直書きせず、以下の方法で管理する。
- Excel: 特定のシートのセル、または非表示の名前付き範囲に格納する。
- Access: 別のテーブルに格納し、必要な時に取得する。
- 環境変数: Windowsの環境変数に設定し、VBAから
Environ()
関数で取得する。
落とし穴
- 複雑なJSONパース: 外部ライブラリがない場合、ネストが深いJSONや動的なキーを持つJSONのパースは正規表現だけでは非常に困難になる。簡易的なJSON構造に限定するか、自前でパースロジックを実装する必要がある。
- 非同期リクエスト:
WinHttpRequest
は非同期モードもサポートするが、VBAではイベントハンドリングが煩雑なため、実務では同期リクエストが一般的である。長時間の処理にはUIフリーズを避ける工夫が必要。
- 文字コード:
WinHttpRequest
はResponseText
プロパティでUTF-8などを自動的に処理するが、まれに文字化けが発生する場合がある。その際はResponseBody
(バイト配列)を取得し、ADODB.Stream
オブジェクトなどで明示的に変換を試みる必要がある。
- タイムアウト: ネットワーク状況によりAPIリクエストが長時間応答しない場合があるため、
WinHttpRequest.SetTimeouts
メソッドで明示的にタイムアウトを設定することが重要である。
- セキュリティ: APIキーなどの認証情報をコード内やファイル内に保存する際は、アクセス制限や暗号化などの対策を講じる必要がある。
まとめ
VBAにおけるREST APIリクエストは、WinHttpRequest
オブジェクトとVBScript.RegExp
、そしてWin32 APIを組み合わせることで、外部ライブラリに依存せず実現可能である。ExcelやAccessといったOfficeアプリケーションの性能特性を理解し、ScreenUpdating
の無効化、配列への一括書き込み、データベーストランザクションといった最適化手法を適用することで、実用的な処理速度を確保できる。これにより、Office環境から外部Webサービスと連携し、業務の自動化とデータ活用の幅を広げられる。
コメント