Excel VBAでOutlookのメールデータを抽出するプログラムを作った時に地味に躓いたり、歯痒いところに手が届かないことが多かったので備忘半分でプログラムを残しています
調べれば調べるほど微調整箇所が多いのでちょくちょく更新するつもりです、最終更新日をご確認ください
このマクロを使うと、Outlookに設定している全アカウントの受信メールと送信メールをまとめてExcelに書き出せます
取得できるのは差出人アドレス・TO・CC・BCC・受信日時・件名・本文・添付ファイルで、フォルダ階層も追いかけて再帰的に拾ってくれます
1通ずつ手作業でコピペするのが面倒だったり、メールの一覧をExcelで分析したい方の役に立てばうれしいです
動作条件(クラシックOutlook専用)
まず最初に大事な前提をお伝えします
このマクロが動くのはクラシックOutlook(旧Outlook)だけです
このコードはクラシックOutlook専用です
新しいOutlook(New Outlook)ではVBAが動きません、Microsoftが公式に「新しいOutlookではVBAとマクロをサポートしない」と明言しています
Outlook.Application や GetNamespace(“MAPI”) といったCOM経由の操作はすべて使えません、新しいOutlookがWebView2ベースの別アーキテクチャに作り替えられたためです
クラシックOutlookは2026年時点で少なくとも2029年まではサポートが続く見込みなので、いま使う分には問題ないです、Outlookウィンドウ右上のトグルでクラシックに切り替えられます
2026年4月ごろから個人向けでは新しいOutlookがデフォルトになり始めていますが、右上のトグルでクラシックへ戻せば今までどおりVBAが使えます
どうしても新しいOutlookで自動化したい場合は、VBAではなくPower AutomateやMicrosoft Graphへ寄せることになります、このあたりはMicrosoftが代替手段として案内しているので、頭の片隅に置いておくと安心です
VBAはバージョンによって動かなくなることがあるので、このプログラムを作成・利用している私の環境はこちらです
| OS | Windows 10 / 11 |
|---|---|
| Outlookの種類 | クラシックOutlook(Microsoft 365 / Office 2021以降、32bit・64bit両対応) |
| 動作対象外 | 新しいOutlook(New Outlook)はVBA非対応のため使えません |
参照設定
プログラムを作り始める前に、まずは参照設定からです
外部ライブラリを読み込んだ方がプログラムを作りやすいので、今回はOutlook用の「Microsoft Outlook 16.0 Object Library」を有効にしてあげます
VBE(Visual Basic Editor)のメニューから「ツール」→「参照設定」と進んで、リストの中からチェックを入れるだけです

全体プログラム
説明はいいからとにかくマクロが欲しいって方へ
標準モジュールに全部コピペすれば使えるはずです
コード全文(コピペ用)
Declare PtrSafe Function SHCreateDirectoryEx Lib "shell32" _
Alias "SHCreateDirectoryExA" _
(ByVal hwnd As LongPtr, _
ByVal pszPath As String, _
ByVal psa As LongPtr) As Long
'---後で編集しやすいように列番号は列挙型で宣言しておく
Enum lngCol
Account = 1
FolderName
SenderEmailAddress
MailTO
MailCC
MailBCC
ReceivedTime
MailSubject
MailBody
AttachmentsCount
End Enum
Public ws As Worksheet
Sub GetOutlookMail()
Dim oApp As New Outlook.Application
Dim oNS As Outlook.Namespace
Dim oFld As Outlook.Folder
'---画面更新処理を止めておく
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
'---出力するシートの設定
Set ws = CreateSheet
ws.Select
ws.Range(Cells(2, 1), Cells(Cells(Rows.Count, 1).End(xlUp).Row + 1, 1)).EntireRow.Delete
'---MAPIをセット
Set oNS = oApp.GetNamespace("MAPI")
For Each oFld In oNS.Folders
'---アカウント名を継承させながらフォルダを再帰処理しながら検索する
Call SearchFolder(oFld.Name, "", oFld)
Next
Set oNS = Nothing
Set ws = Nothing
'---画面更新処理を復旧させる
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
'---終了メッセージ
MsgBox "更新完了しました"
End Sub
Private Function SearchFolder(Account As String, FolderPath As String, TargetFolder As Outlook.Folder)
'==============================
'メールフォルダを再帰処理で検索する
'==============================
Dim oFld As Outlook.Folder
'---同期の問題と同期の失敗フォルダを除外する処置
If TargetFolder.Name Like "同期の*" Then
Exit Function
End If
'---フォルダタイプがメールだったら中のメール情報を取得する
If TargetFolder.DefaultItemType = olMailItem Then
Call GetMailData(Account, FolderPath, TargetFolder)
End If
'---フォルダ情報をパンくず形式で継承しつつ再帰処理させる
For Each oFld In TargetFolder.Folders
Call SearchFolder(Account, FolderPath & IIf(Len(FolderPath) > 0, " > ", "") & oFld.Name, oFld)
Next
End Function
Private Function GetMailData(Account As String, TargetFolderName As String, TargetFolder As Outlook.Folder)
'==============================
'対象フォルダからメール情報を取得してエクセルに反映させる
'==============================
Dim oItems As Outlook.Items
Dim oItem As Outlook.MailItem
Dim oAtt As Outlook.Attachment
Dim i As Long
Dim lngRow As Long
Dim strFilter
Dim savePath As String
With ws
.Select
Set oItems = TargetFolder.Items
'---ステータスバーに取得フォルダ等を表示する
Application.StatusBar = "「" & Account & "」の「" & TargetFolderName & "」から" & oItems.Count & "件のデータ取得中・・・"
'---取得したメールを反映させる
For i = 1 To oItems.Count
'---データがメールタイプじゃない可能性があるので確認して取得する
If oItems(i).Class = olMail Then
'---入力行(最下行の次)を取得する
lngRow = .Cells(Rows.Count, 1).End(xlUp).Row + 1
'---メールアイテムを変数にセットする
Set oItem = oItems(i)
.Cells(lngRow, lngCol.Account).Value = Account
.Cells(lngRow, lngCol.FolderName).Value = TargetFolderName
.Cells(lngRow, lngCol.SenderEmailAddress).Value = oItem.SenderEmailAddress
.Cells(lngRow, lngCol.MailTO).Value = SplitMailStr(oItem.To, oItem.Recipients)
.Cells(lngRow, lngCol.MailCC).Value = SplitMailStr(oItem.CC, oItem.Recipients)
.Cells(lngRow, lngCol.MailBCC).Value = SplitMailStr(oItem.BCC, oItem.Recipients)
.Cells(lngRow, lngCol.ReceivedTime).Value = oItem.ReceivedTime
With .Cells(lngRow, lngCol.MailSubject)
.NumberFormatLocal = "@" '---先頭文字が=(イコール)などで始まるとエラーになるから表示形式を文字列にする
.Value = oItem.Subject
.WrapText = False '---折り返し表示すると動作が重くなるので解除する
.NumberFormatLocal = "G/標準" '---表示形式を標準に戻す
End With
With .Cells(lngRow, lngCol.MailBody)
.NumberFormatLocal = "@" '---本文が=(イコール)で始まるとエラーになるから表示形式を文字列にする
.Value = oItem.Body
.WrapText = False '---折り返し表示すると動作が重くなるので解除する
.NumberFormatLocal = "G/標準" '---表示形式を標準に戻す
End With
'---添付ファイルを取得する
.Cells(lngRow, lngCol.AttachmentsCount).Value = oItem.Attachments.Count
For Each oAtt In oItem.Attachments
'---受信日時をベースにフルパスフォルダ名を決める
savePath = ThisWorkbook.Path & "\att\" & Format(oItem.ReceivedTime, "yyyymmdd_hhmm")
'---多重階層フォルダを作成する
rc = SHCreateDirectoryEx(0&, savePath, 0&)
'---ここで実際にフォルダを作成する
oAtt.SaveAsFile savePath & "\" & oAtt.Filename
Next
'---内外全てにグレー罫線を引く
With .Range(Cells(lngRow, lngCol.Account), Cells(lngRow, lngCol.AttachmentsCount)).Borders()
.LineStyle = xlContinuous
.Weight = xlThin
.Color = RGB(128, 128, 128)
End With
Set oItem = Nothing
End If
Next
'---ステータスバーをリセットする
Application.StatusBar = False
End With
End Function
Private Function SplitMailStr(MailStr As String, RecipientsData As Recipients)
'==============================
'宛先が複数ある場合は;(セミコロン)区切りになっているので一度分解してChangeAliasで変換した後に結合する
'==============================
Dim i As Long, MailArray As Variant
MailArray = Split(MailStr, ";")
For i = LBound(MailArray) To UBound(MailArray)
MailArray(i) = ChangeAlias(MailArray(i), RecipientsData)
Next
SplitMailStr = Join(MailArray, ";")
End Function
Private Function ChangeAlias(Alias As Variant, RecipientsData As Recipients)
'==============================
'宛先情報がエイリアスになっている場合があるからメアドに変換する
'==============================
Dim RecipientData As Recipient
'---@が含まれているならメアドなのでそのまま
If Alias Like "*@*" Then
ChangeAlias = Alias
Exit Function
End If
'---表示名が受信者情報と一致していたらアドレスに変換する
For Each RecipientData In RecipientsData
If Trim(RecipientData.Name) = Trim(Alias) Then
ChangeAlias = RecipientData.Address
Exit Function
End If
Next
'---変換できなかったらそのまま返す ※ココに到達することはないはず
ChangeAlias = Alias
End Function
Private Function CreateSheet(Optional SheetName = "OUTPUT") As Worksheet
Set CreateSheet = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count))
With CreateSheet
'---シート名を変えるけど重複する可能性があるのでエラー無視する
On Error Resume Next
.Name = SheetName
On Error GoTo 0
'---ヘッダー部の設定
.Cells(1, lngCol.Account).Value = "Account"
.Cells(1, lngCol.FolderName).Value = "FolderName"
.Cells(1, lngCol.SenderEmailAddress).Value = "SenderEmailAddress"
.Cells(1, lngCol.MailTO).Value = "MailTO"
.Cells(1, lngCol.MailCC).Value = "MailCC"
.Cells(1, lngCol.MailBCC).Value = "MailBCC"
.Cells(1, lngCol.ReceivedTime).Value = "ReceivedTime"
.Cells(1, lngCol.MailSubject).Value = "MailSubject"
.Cells(1, lngCol.MailBody).Value = "MailBody"
.Cells(1, lngCol.AttachmentsCount).Value = "AttachmentsCount"
With .Range(Cells(1, lngCol.Account), Cells(1, lngCol.AttachmentsCount))
.EntireColumn.ColumnWidth = 19.88
.Interior.Color = RGB(191, 191, 191)
With .Borders()
.LineStyle = xlContinuous
.Weight = xlThin
.Color = RGB(128, 128, 128)
End With
End With
End With
End Functionプロシージャ別の説明
ここからはプロシージャ別に分解して、少し詳しく説明します
共通部分
Declare PtrSafe Function SHCreateDirectoryEx Lib "shell32" _
Alias "SHCreateDirectoryExA" _
(ByVal hwnd As LongPtr, _
ByVal pszPath As String, _
ByVal psa As LongPtr) As Long
'---後で編集しやすいように列番号は列挙型で宣言しておく
Enum lngCol
Account = 1
FolderName
SenderEmailAddress
MailTO
MailCC
MailBCC
ReceivedTime
MailSubject
MailBody
AttachmentsCount
End Enum
Public ws As WorksheetSHCreateDirectoryExは多重階層でフォルダを作成する時のWindows APIです
添付ファイルを保存する時に使うので宣言しておきます
lngColは列挙型で列番号を宣言しておくとプログラムが作りやすい気がする、個人的なお作法です
wsはプロシージャを跨いで使うのでPublicで宣言しておきます
GetOutlookMail
Sub GetOutlookMail()
Dim oApp As New Outlook.Application
Dim oNS As Outlook.Namespace
Dim oFld As Outlook.Folder
'---画面更新処理を止めておく
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
'---出力するシートの設定
Set ws = CreateSheet
ws.Select
ws.Range(Cells(2, 1), Cells(Cells(Rows.Count, 1).End(xlUp).Row + 1, 1)).EntireRow.Delete
'---MAPIをセット
Set oNS = oApp.GetNamespace("MAPI")
For Each oFld In oNS.Folders
'---アカウント名を継承させながらフォルダを再帰処理しながら検索する
Call SearchFolder(oFld.Name, "", oFld)
Next
Set oNS = Nothing
Set ws = Nothing
'---画面更新処理を復旧させる
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
'---終了メッセージ
MsgBox "更新完了しました"
End Subメインのプロシージャです
これ自体にはたいして説明する部分はないですが、画面更新を止めるのは個人的なお作法です
ポイントはGetNamespace(“MAPI”)で、ここでOutlookのデータ階層にアクセスして、アカウントごとのフォルダを順番にたどっていきます
SearchFolder
Private Function SearchFolder(Account As String, FolderPath As String, TargetFolder As Outlook.Folder)
'==============================
'メールフォルダを再帰処理で検索する
'==============================
Dim oFld As Outlook.Folder
'---同期の問題と同期の失敗フォルダを除外する処置
If TargetFolder.Name Like "同期の*" Then
Exit Function
End If
'---フォルダタイプがメールだったら中のメール情報を取得する
If TargetFolder.DefaultItemType = olMailItem Then
Call GetMailData(Account, FolderPath, TargetFolder)
End If
'---フォルダ情報をパンくず形式で継承しつつ再帰処理させる
For Each oFld In TargetFolder.Folders
Call SearchFolder(Account, FolderPath & IIf(Len(FolderPath) > 0, " > ", "") & oFld.Name, oFld)
Next
End FunctionOutlookがアカウントを起点としたフォルダ構成になっているので、再帰処理で順番にフォルダを巡りながら、そのフォルダからメール情報を取得するようにしています
フォルダの中には連絡先などメールフォルダではないものがあるので、メールを取得する対象かどうかDefaultItemTypeで判定しています
GetMailData
Private Function GetMailData(Account As String, TargetFolderName As String, TargetFolder As Outlook.Folder)
'==============================
'対象フォルダからメール情報を取得してエクセルに反映させる
'==============================
Dim oItems As Outlook.Items
Dim oItem As Outlook.MailItem
Dim oAtt As Outlook.Attachment
Dim i As Long
Dim lngRow As Long
Dim rc As Long
Dim strFilter
Dim savePath As String
With ws
.Select
Set oItems = TargetFolder.Items
'---ステータスバーに取得フォルダ等を表示する
Application.StatusBar = "「" & Account & "」の「" & TargetFolderName & "」から" & oItems.Count & "件のデータ取得中・・・"
'---取得したメールを反映させる
For i = 1 To oItems.Count
'---データがメールタイプじゃない可能性があるので確認して取得する
If oItems(i).Class = olMail Then
'---入力行(最下行の次)を取得する
lngRow = .Cells(Rows.Count, 1).End(xlUp).Row + 1
'---メールアイテムを変数にセットする
Set oItem = oItems(i)
.Cells(lngRow, lngCol.Account).Value = Account
.Cells(lngRow, lngCol.FolderName).Value = TargetFolderName
.Cells(lngRow, lngCol.SenderEmailAddress).Value = oItem.SenderEmailAddress
.Cells(lngRow, lngCol.MailTO).Value = SplitMailStr(oItem.To, oItem.Recipients)
.Cells(lngRow, lngCol.MailCC).Value = SplitMailStr(oItem.CC, oItem.Recipients)
.Cells(lngRow, lngCol.MailBCC).Value = SplitMailStr(oItem.BCC, oItem.Recipients)
.Cells(lngRow, lngCol.ReceivedTime).Value = oItem.ReceivedTime
With .Cells(lngRow, lngCol.MailSubject)
.NumberFormatLocal = "@" '---先頭文字が=(イコール)などで始まるとエラーになるから表示形式を文字列にする
.Value = oItem.Subject
.WrapText = False '---折り返し表示すると動作が重くなるので解除する
.NumberFormatLocal = "G/標準" '---表示形式を標準に戻す
End With
With .Cells(lngRow, lngCol.MailBody)
.NumberFormatLocal = "@" '---本文が=(イコール)で始まるとエラーになるから表示形式を文字列にする
.Value = oItem.Body
.WrapText = False '---折り返し表示すると動作が重くなるので解除する
.NumberFormatLocal = "G/標準" '---表示形式を標準に戻す
End With
'---添付ファイルを取得する
.Cells(lngRow, lngCol.AttachmentsCount).Value = oItem.Attachments.Count
For Each oAtt In oItem.Attachments
'---受信日時をベースにフルパスフォルダ名を決める
savePath = ThisWorkbook.Path & "\att\" & Format(oItem.ReceivedTime, "yyyymmdd_hhmm")
'---多重階層フォルダを作成する
rc = SHCreateDirectoryEx(0&, savePath, 0&)
'---ここで実際にフォルダを作成する
oAtt.SaveAsFile savePath & "\" & oAtt.Filename
Next
'---内外全てにグレー罫線を引く
With .Range(Cells(lngRow, lngCol.Account), Cells(lngRow, lngCol.AttachmentsCount)).Borders()
.LineStyle = xlContinuous
.Weight = xlThin
.Color = RGB(128, 128, 128)
End With
Set oItem = Nothing
End If
Next
'---ステータスバーをリセットする
Application.StatusBar = False
End With
End Functionこれがズバリ、メール情報の取得部分でポイントが何点か・・・
For Nextでループしているとメールタイプじゃないアイテムも検知してしまい、型エラーの原因になるのでClassでメールかどうか判断しています
TO・CC・BCCはそのままだと連絡先のエイリアスが反映してしまい、メアドがわからなくなってしまうので、サブプロシージャで変換するようにします
件名と本文はExcelに反映させた時、標準書式の状態で=や@が先頭だと数式判定になってエラーになるので、反映前に文字列化→反映→標準書式へ戻す手順で反映させています
添付ファイルを取得する時に、メールとの紐づきがわかるように受信日時のフォルダを作成してそこに保存するようにしていますが、判断しにくいなら適当に編集してもらえれば大丈夫です
SplitMailStr
Private Function SplitMailStr(MailStr As String, RecipientsData As Recipients)
'==============================
'宛先が複数ある場合は;(セミコロン)区切りになっているので一度分解してChangeAliasで変換した後に結合する
'==============================
Dim i As Long, MailArray As Variant
MailArray = Split(MailStr, ";")
For i = LBound(MailArray) To UBound(MailArray)
MailArray(i) = ChangeAlias(MailArray(i), RecipientsData)
Next
SplitMailStr = Join(MailArray, ";")
End Function次のChangeAliasのためのサブプロシージャです
TOやCCに複数名設定されているとセミコロン(;)をデリミタで結合された状態になっていて、エイリアス→メアドの変換がうまくできないので、分解してそれぞれにChangeAliasします
ChangeAlias
Private Function ChangeAlias(Alias As Variant, RecipientsData As Recipients)
'==============================
'宛先情報がエイリアスになっている場合があるからメアドに変換する
'==============================
Dim RecipientData As Recipient
'---@が含まれているならメアドなのでそのまま
If Alias Like "*@*" Then
ChangeAlias = Alias
Exit Function
End If
'---表示名が受信者情報と一致していたらアドレスに変換する
For Each RecipientData In RecipientsData
If Trim(RecipientData.Name) = Trim(Alias) Then
ChangeAlias = RecipientData.Address
Exit Function
End If
Next
'---変換できなかったらそのまま返す ※ココに到達することはないはず
ChangeAlias = Alias
End FunctionGetMailDataにも書いた通り、エイリアスをメアドに変える処理です
Recipientsの中にエイリアスとメアドのセット情報があるので、変換元になるエイリアスと一致したらメアドを返してあげます
CreateSheet
Private Function CreateSheet(Optional SheetName = "OUTPUT") As Worksheet
Set CreateSheet = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count))
With CreateSheet
'---シート名を変えるけど重複する可能性があるのでエラー無視する
On Error Resume Next
.Name = SheetName
On Error GoTo 0
'---ヘッダー部の設定
.Cells(1, lngCol.Account).Value = "Account"
.Cells(1, lngCol.FolderName).Value = "FolderName"
.Cells(1, lngCol.SenderEmailAddress).Value = "SenderEmailAddress"
.Cells(1, lngCol.MailTO).Value = "MailTO"
.Cells(1, lngCol.MailCC).Value = "MailCC"
.Cells(1, lngCol.MailBCC).Value = "MailBCC"
.Cells(1, lngCol.ReceivedTime).Value = "ReceivedTime"
.Cells(1, lngCol.MailSubject).Value = "MailSubject"
.Cells(1, lngCol.MailBody).Value = "MailBody"
.Cells(1, lngCol.AttachmentsCount).Value = "AttachmentsCount"
With .Range(Cells(1, lngCol.Account), Cells(1, lngCol.AttachmentsCount))
.EntireColumn.ColumnWidth = 19.88
.Interior.Color = RGB(191, 191, 191)
With .Borders()
.LineStyle = xlContinuous
.Weight = xlThin
.Color = RGB(128, 128, 128)
End With
End With
End With
End Function出力先になるシートをセットします
ヘッダーの文字に書式と罫線を設定していますが、内容はお好みでどうぞ
あとがき
ChangeAliasをはじめ、満足な結果を得るためには面倒な箇所があったので、地味に時間がかかりました・・
実際にExcelでマクロにするときは取得対象期間を設定できるようにしたり改修の余地はあるのですが、プログラムだけで公開するとややこしくなるのでここでは反映していません
私はこのマクロで目的を達成しましたが、他にも取得できる情報はあるので、上記プログラムでは不十分な方がいればコメントいただけると改修して更新しますね
VBAでメールまわりを触る話だと、送信側を自動化したい方も多いと思います、GmailをVBAから送る方法はVBAでGmailを送信する(CDO)でまとめているので、あわせてどうぞ



コメント