【VBA】OutlookのメールデータをExcelに取得するマクロ

OutlookのメールデータをVBAで取得するアイキャッチ

Excel VBAでOutlookのメールデータを抽出するプログラムを作った時に地味に躓いたり、歯痒いところに手が届かないことが多かったので備忘半分でプログラムを残しています

調べれば調べるほど微調整箇所が多いのでちょくちょく更新するつもりです、最終更新日をご確認ください

このマクロを使うと、Outlookに設定している全アカウントの受信メールと送信メールをまとめてExcelに書き出せます

取得できるのは差出人アドレス・TO・CC・BCC・受信日時・件名・本文・添付ファイルで、フォルダ階層も追いかけて再帰的に拾ってくれます

1通ずつ手作業でコピペするのが面倒だったり、メールの一覧をExcelで分析したい方の役に立てばうれしいです

目次

動作条件(クラシックOutlook専用)

まず最初に大事な前提をお伝えします

このマクロが動くのはクラシックOutlook(旧Outlook)だけです

このコードはクラシックOutlook専用です

新しいOutlook(New Outlook)ではVBAが動きません、Microsoftが公式に「新しいOutlookではVBAとマクロをサポートしない」と明言しています

Outlook.ApplicationGetNamespace(“MAPI”) といったCOM経由の操作はすべて使えません、新しいOutlookがWebView2ベースの別アーキテクチャに作り替えられたためです

クラシックOutlookは2026年時点で少なくとも2029年まではサポートが続く見込みなので、いま使う分には問題ないです、Outlookウィンドウ右上のトグルでクラシックに切り替えられます

2026年4月ごろから個人向けでは新しいOutlookがデフォルトになり始めていますが、右上のトグルでクラシックへ戻せば今までどおりVBAが使えます

どうしても新しいOutlookで自動化したい場合は、VBAではなくPower AutomateやMicrosoft Graphへ寄せることになります、このあたりはMicrosoftが代替手段として案内しているので、頭の片隅に置いておくと安心です

VBAはバージョンによって動かなくなることがあるので、このプログラムを作成・利用している私の環境はこちらです

OSWindows 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)のメニューから「ツール」→「参照設定」と進んで、リストの中からチェックを入れるだけです

VBEの参照設定でMicrosoft Outlook 16.0 Object Libraryにチェックを入れる画面

全体プログラム

説明はいいからとにかくマクロが欲しいって方へ

標準モジュールに全部コピペすれば使えるはずです

コード全文(コピペ用)
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 Worksheet

SHCreateDirectoryExは多重階層でフォルダを作成する時の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 Function

Outlookがアカウントを起点としたフォルダ構成になっているので、再帰処理で順番にフォルダを巡りながら、そのフォルダからメール情報を取得するようにしています

フォルダの中には連絡先などメールフォルダではないものがあるので、メールを取得する対象かどうか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 Function

GetMailDataにも書いた通り、エイリアスをメアドに変える処理です

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)でまとめているので、あわせてどうぞ

📚 VBAの独学に効く本PR
Excel VBA塾【動画×本で学ぶ!】

Excel VBA塾【動画×本で学ぶ!】

たてばやし淳

パーフェクトExcel VBA

パーフェクトExcel VBA

高橋宣成

Excel マクロ&VBA[実践ビジネス入門講座]完全版 第3版

Excel マクロ&VBA[実践ビジネス入門講座]完全版 第3版

国本温子

私のおすすめからランダムで3冊を表示しています


最後に・・・

クラウドワークスココナラでお仕事受け付けています!

PythonとExcelを中心に仕事に役立つ業務ツールや自動化、スクレイピングツールの作成を受注していて、クラウドワークスでは気が付けば100件以上のお仕事を受注してきました!

会社員をやりながらの副業なので時間の捻出は相応ですが、クライアントの方々と近い立場でこちらからも提案しながら活動していますのでお悩みあれば是非ご相談ください

ココナラのプロフィールページへ

"ココナラ"に新規登録する際は1,000Pもらえる紹介コード使ってください

78E62K

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

VBAとPythonを中心にユーザー側でできるITを自己学習しているので備忘録半分、学習履歴を残して同じ道を辿る人の参考になればとブログを始めました

副業でスクレイピングツール作成を中心にできることを色々やっていますのでご相談いただけるとありがたいです!


クラウドワークスのページへ


ココナラのページへ

コメント

コメントする

目次