業務効率化

エクセルVBA:Outlookメール文から宛先抽出

Outlookで選択したメールの宛先を抽出するマクロ関数です。選択したメールの親フォルダに応じて送信者と受信者を判定し、適切な宛先名を抽出します。メール文頭、送信者表記名(Senderプロパティ)、TOメアド及び、メール親フォルダそれぞれを判定条件し、意図した宛先抽出が可能です。

また、DearとFromをキーワードに英語の場合は「Eng 名前」として抽出することでその後の処理で活用できます。https://gakuhenn.com/vba-chatgptapi-outlookmail-system/で紹介したメール文作成システムのようなマクロで活用できます。

記事の内容

・挙動

・コード

・ポイント機能

・挙動

対象のメールを選択した状態でプロシージャを実行します。親フォルダ(受信フォルダ、曹仁済みフォルダ)及び、メール文頭の内容から、宛先●●さんを取得します。

受信ボックスのメール選択した例

受信ボックスではなく、送信済みボックス上のメールに対して実行した場合、送信者は田中=あなたになるため、●●は佐藤さんとして抽出されます。

・コード

➤選択メールの宛先を表示するプロシージャこちら

Option Explicit

Sub fChkParentFolder_Sub()

    Dim olApp As Outlook.Application
    Dim olMail As Outlook.MailItem
    Dim olSelection As Outlook.Selection
    Dim Mailbody As String
    
    Dim olFolder As Folder
    Dim parentFolder As Object
    Dim folderPath As String
    Dim MSenderOrMReceiver As String

    ' Outlook から選択メールを取得
    Set olApp = Outlook.Application
    Set olSelection = olApp.ActiveExplorer.Selection

    If olSelection.count = 0 Then
        MsgBox "メールが選択されていません。"
        Exit Sub
    End If

    If Not TypeOf olSelection.Item(1) Is Outlook.MailItem Then
        MsgBox "選択されたアイテムはメールではありません。"
        Exit Sub
    End If

    Set olMail = olSelection.Item(1)
    Mailbody = olMail.body

    Set olFolder = olMail.Parent
    folderPath = olFolder.Name

    Set parentFolder = olFolder
    Do While Not parentFolder.Parent Is Nothing
        Set parentFolder = parentFolder.Parent
        If TypeOf parentFolder Is Outlook.Folder Then
            folderPath = parentFolder.Name & "\" & folderPath
        Else
            Exit Do
        End If
    Loop

    If InStr(1, folderPath, "受信", vbTextCompare) > 0 Then
        MSenderOrMReceiver = "MSender"
    ElseIf InStr(1, folderPath, "送信", vbTextCompare) > 0 Then
        MSenderOrMReceiver = "MReceiver"
    Else
        MSenderOrMReceiver = "MSender"
    End If

    ' 名前抽出を実行
    Dim result As String
    result = fExtractNameInMail(MSenderOrMReceiver, Mailbody, olMail, "英語であなたの苗字", "日本語であなたの苗字")
    MsgBox "宛先: " & result

End Sub



Function fExtractNameInMail(MSenderOrMReceiver As String, Mailbody As Variant, mailsent As Outlook.MailItem, YouLstName As String, YouLstNameJp As String) As String
    Dim olApp As Outlook.Application
    Dim olMail As Outlook.MailItem
    Dim olSelection As Selection
    Dim bodyText As String
    Dim MailSender As String
    Dim lines As Variant
    Dim namefrom As String
    Dim KeywordsArray() As Variant
    Dim MailSenderAdr As String
    
    
    bodyText = Mailbody
    
    ' 本文を改行ごとに分割
    lines = Split(bodyText, vbCrLf)
    
    If InStr(1, MSenderOrMReceiver, "MSender", vbTextCompare) > 0 Then
        KeywordsArray = Array("です", "from", ",")
    ElseIf InStr(1, MSenderOrMReceiver, "MReceiver", vbTextCompare) > 0 Then
        KeywordsArray = Array("です", "Dear", ",")
    End If
    
    ' 先頭3行を結合(4行)
    Dim i As Integer, namefrombuf As String
    For i = LBound(lines) To Application.WorksheetFunction.Min(UBound(lines), 4)
        If i >= 0 Then
            
            namefrom = namefrom & " " & lines(i)
            namefrombuf = namefrom
            If InStr(1, namefrom, "です", vbTextCompare) Or InStr(1, namefrom, "申します", vbTextCompare) Then
                i = 100
                namefrom = Trim(namefrom)
                Exit For
            ElseIf InStr(1, namefrom, "from", vbTextCompare) Then
                namefrom = Trim(namefrom)
                i = 101
                Exit For
                
            'Fromが改行されている場合
            ElseIf InStr(1, namefrom, ",", vbTextCompare) Then
                namefrom = Trim(namefrom)
                Dim ii As Long
                 For ii = i To 2
                    
                    namefrom = namefrom & " " & lines(ii + 1)
                    namefrombuf = namefrom
                    
                    If InStr(1, namefrom, "from", vbTextCompare) Then
                        i = 101
                        namefrom = Trim(namefrom)
                        Exit For
                    End If
                    
                 Next
                Exit For
            End If
        End If
    Next i

    'メール本文が「●●さん、●●です」以外で宛先抽出できない場合
    '送信者表示名,シート上のあなたの名前から宛先名を判断
    
    If i <> 101 And i <> 100 Then
       'あなたの名前がnameinmail=本文冒頭に「Dear あなたの名前」のように入っている場合、MailsenderNameを宛先に
       If InStr(1, namefrom, YouLstName, vbTextCompare) > 0 Or InStr(1, namefrom, YouLstNameJp, vbTextCompare) > 0 Then
            MailSenderAdr = mailsent.SenderName
       Else
            'それ以外の場合To宛先の人を宛先にする
            MailSenderAdr = mailsent.To
       End If
       
       If IsJPTexts(namefrom, "0.1") Then
    
        
        fExtractNameInMail = GetLastNameInMailAdr(MailSenderAdr, "ja")
        
        Else
        
        fExtractNameInMail = GetLastNameInMailAdr(MailSenderAdr, "en")
        
        End If
        Exit Function
    
    End If
    
     
     '’’’’’’’’’’’’’’’’’’’’’’’’’' 英語メールの場合処理追加必要’’’’’’’’’’’’’’’’’’’’’’’’’
     'MsgBox namefrom
    
    If i = 101 And InStr(1, MSenderOrMReceiver, "MSender", vbTextCompare) > 0 Then
        fExtractNameInMail = Mid(namefrom, InStr(1, namefrom, "from", vbTextCompare) + 5)
        Exit Function
    
    ElseIf InStr(1, namefrom, "Dear", vbTextCompare) > 0 And InStr(1, namefrom, "from", vbTextCompare) > 0 Then
        fExtractNameInMail = Mid(namefrom, InStr(1, namefrom, "Dear", vbTextCompare) + 5, InStr(1, namefrom, "san", vbTextCompare) - (InStr(1, namefrom, "Dear", vbTextCompare) + 5))
        Exit Function
    
    ElseIf InStr(1, namefrom, "Dear", vbTextCompare) > 0 And InStr(1, namefrom, ",", vbTextCompare) > 0 Then
    ' 送信者に送りたいときで、from送信者のような記載もない場合の名前抽出は例外的記述の多さにより困難
        fExtractNameInMail = "EngName "
        Exit Function
    ElseIf InStr(1, namefrom, "san", vbTextCompare) > 0 Or InStr(1, namefrom, "Dear", vbTextCompare) > 0 Then
        fExtractNameInMail = "EngName "
        Exit Function
    End If
    
    

    
    Dim TrimedNamefrom As String
    
    KeywordsArray = Array(" ", " ", "、", vbCrLf, vbLf, vbCr, vbTab)
    TrimedNamefrom = namefrom

    TrimedNamefrom = Removekeywords(TrimedNamefrom, KeywordsArray)
    
    TrimedNamefrom = CompareToExtract(MSenderOrMReceiver, namefrom, TrimedNamefrom)
    'Debug.Print TrimedNamefrom
    
    KeywordsArray = Array("です。", "です", "。", "、", "さん", " 様", " 様", " 課長", " 課長", " 部長", " 部長")
    'Debug.Print Removekeywords(TrimedNamefrom, KeywordsArray)
    TrimedNamefrom = Removekeywords(Trim(TrimedNamefrom), KeywordsArray)
    If InStr(1, MSenderOrMReceiver, "MSender", vbTextCompare) > 0 Then
        TrimedNamefrom = Mid(TrimedNamefrom, InStrRev(TrimedNamefrom, " ", , vbTextCompare) + 1)
    End If
    fExtractNameInMail = Trim(TrimedNamefrom)
    'MsgBox fExtractNameInMail

End Function


Function Removekeywords(text As String, removewords As Variant) As String
    Dim patterns As Variant
    Dim i As Integer

    ' 削除する文字列のリスト
    patterns = removewords
    
    ' 配列をループして置換
    For i = LBound(patterns) To UBound(patterns)
        text = Replace(text, patterns(i), "")
    Next i
    
    ' 結果を返す
    Removekeywords = text
End Function

Function CompareToExtract(MSenderOrMReceiver As String, original As String, trimmed As String) As String
    Dim i As Long
    Dim lengthOriginal As Long
    Dim lengthTrimmed As Long
    
    ' 元の文字列と削除後の文字列の長さを取得
    lengthOriginal = Len(original)
    lengthTrimmed = Len(trimmed)
    
    ' 短い方の長さまで比較
    For i = 1 To Application.WorksheetFunction.Min(lengthOriginal, lengthTrimmed)
        ' 先頭から異なる文字が見つかった場合
        If Mid(original, i, 1) <> Mid(trimmed, i, 1) Then
            If InStr(1, MSenderOrMReceiver, "MSender", vbTextCompare) > 0 Then
                ' その位置から文末までの文字列を削除
                CompareToExtract = Mid(original, i)
                Exit Function
            ElseIf InStr(1, MSenderOrMReceiver, "MReceiver", vbTextCompare) > 0 Then
                ' 先頭からその位置までの文字列を削除
                CompareToExtract = Left(original, i)
                Exit Function
            Else
                ' 先頭からその位置までの文字列を削除
                CompareToExtract = Mid(original, i)
                Exit Function
            End If
        End If
    Next i
    
    ' 異なる部分が見つからない場合
    CompareToExtract = original
End Function
Function GetLastNameInMailAdr( _
    Mailaddress As String, _
    Optional lang_JPorEN As String = "en" _
) As String

    Dim eng As String
    Dim jp As String
    Dim pos1 As Long, pos2 As Long
    Dim result As String
    Dim hasBracket As Boolean

    ' ()位置
    pos1 = InStr(Mailaddress, "(")
    pos2 = InStr(Mailaddress, ")")
    hasBracket = (pos1 > 0 And pos2 > pos1)

    ' =========================
    ' ()があるケース ()の中に日本語名がある表式を想定
    ' =========================
    
    If hasBracket Then

        ' 英語部分
        eng = Trim(Left(Mailaddress, pos1 - 1))

        ' 日本語部分
        jp = Mid(Mailaddress, pos1 + 1, pos2 - pos1 - 1)

        If LCase(lang_JPorEN) = "jp" Or LCase(lang_JPorEN) = "ja" Then
            jp = Replace(jp, " ", " ")
            GetLastNameInMailAdr = Split(Trim(jp), " ")(0)

        Else
            result = Split(Trim(eng), " ")(0)
            GetLastNameInMailAdr = StrConv(LCase(result), vbProperCase)
        End If

    ' =========================
    ' ()が無いケース
    ' =========================
    Else
        
        ' 英語(先頭単語)
        On Error Resume Next
        Mailaddress = Mid(Mailaddress, 1, InStr(1, Mailaddress, "@", vbTextCompare) - 1)
        eng = Mailaddress
        On Error GoTo 0
        
        ' < より前だけ使う(メール部分除去)
        If InStr(eng, "<") > 0 Then
            eng = Left(eng, InStr(eng, "<") - 1)
        End If

        result = Split(Trim(eng), " ")(0)
        GetLastNameInMailAdr = StrConv(LCase(result), vbProperCase)
        
        '日本語指定されている場合でメール文から日本語取得できない場合は翻訳
        'If LCase(lang_JPorEN) = "jp" Or LCase(lang_JPorEN) = "ja" Then
        
        'On Error Resume Next
        '    GetLastNameInMailAdr = TranslateTexts_DefJPandEN(GetLastNameInMailAdr, "en", "jp")
        'On Error GoTo 0
        'End If
        
    End If

End Function

Function GetSMTPAddress(mail As Outlook.MailItem) As String

    Dim sender As Outlook.AddressEntry
    Dim exchUser As Outlook.ExchangeUser
    
    Set sender = mail.sender

    If sender Is Nothing Then Exit Function

    ' Exchangeユーザーの場合
    If sender.Type = "EX" Then
        Set exchUser = sender.GetExchangeUser
        
        If Not exchUser Is Nothing Then
            GetSMTPAddress = exchUser.PrimarySmtpAddress
        Else
            GetSMTPAddress = ""
        End If

    Else
        ' 通常SMTP
        GetSMTPAddress = mail.SenderEmailAddress
    End If

End Function






単純に選択メール送信者のメアドを取得する場合は、Olmailのsenderプロパティーを取得することで、メールの送信者を取得できます。この場合、exchangeの表記設定によっては和英が混在していない場合があり、和文の名前が抽出できないこともあります。

メール文頭での名前取得を基本に、どのメールでも宛先取得ができるように、今スクリプトでは、メールでの文頭、送信者プロパティ、TOメアド及び、選択メールのボックスを条件に宛先を取得しています。

メール文頭が「○○さん  ●●です。」の場合選択したメールぼっくに応じて○○か●●が取得されます。
メール文頭が「○○様/部長」や「●●と申します。」などのように異なる表記であっても、送信者の表記名から(Oultook上で表示されているメアド表示)形式から和英双方の苗字を取得可能です。

・ポイント機能

・選択メールの親フォルダ:ParentFolderの取得

・選択メールのボックスに応じた宛先抽出

ParentFolderの取得

Outlookのメールはそれぞれに対してウィンドウズファイルと同じようにパスの情報があります。そのルートフォルダになる場所に今回使用する受信ボックスと、送信済みボックスの二つの場所があります。今回は送信済みメールに返信するのであれば、メールの宛先を宛先とし、受信ボックスメールに返信するのであれば、メール送信者を宛先にします

そこで、parentFolder変数に選択したメールの親フォルダを親フォルダがなくなるまでDo whileで継続します。そうして得られるパスには、受信ボックスと、送信済みフォルダの文字が入ったパスになります。

これを利用し、選択したメールが受信か送信済みボックスどちらか判定に使います。Instrで単純に一部ワードを検索し含まれているか判定します。

受信ボックスならMSender、送信済みボックスならMReceiver、それ以外はMSenderとして関数に代入します。これを宛先抽出関数で使用します。

選択メールのボックスに応じた宛先抽出

宛先抽出関数ではいくつかの関数を使用します。必要なのは、MSenderかMReceiverかと、選択したメールの本文です。まず、全体文を改行ごとに配列化します。

そして、メール冒頭から検索し、「です」か「from」がある行まで抽出します(○○さん ●●です/ Dear Mr. ○○, from ●● のような文頭を想定)。

それ以外の場合で、

・「です」のみが存在する場合

・文頭無し&本文のみの場合に対しても、分岐処理により宛先を適切に抽出可能です。

メール文頭が「○○さん ●●です/ Dear Mr. ○○, from ●● のような場合」

です/from行を取得することで宛先と送信者が含まれた一行を得られるというわけです。

まず英語のメールであるか判断処理をします。Dear,Fromというキーワードが入っていれば、英文のメールであると判断し、宛先となる部分を抜き出します。

MSenderorReceiverがSenderであれば、送信者に送るということなので、From以降の●●を宛先として、「Eng ●●」の形で変数に格納します。これにより、後々Eng を削除し宛先のみを抽出します。

もし上記の英語要処理に当てはまらない場合は日本語の文と見なします。「○○さん ●●です」の文から、○○、●●を抽出するために元の文章と調整した文章を用意します。後者は、空欄や句読点を取り除いたものです。Removekeywords関数で指定したKeywordsArrayに入った値をすべて削除します。

二つの文章および、MSenderorMReceiverを引数に、CompareToExtraxt関数を実行します。関数では、二つの文字の先頭および中部から文字を比較し、異なる箇所があった時点でその場所を抜き出します。

「○○さん ●●です」から「○○さん●●です」になり、空欄の箇所が抜出のポイントとなります。

その後取得した値をさらに形成します。上記のままだと敬称等がついており、名前だけの抽出ができていません。そこで再度RemoveKeywords関数で余分な文字を指定し、削除します。今回は「です。、さん」などを削除しています。これにより純粋な宛先名のみ取得することができます。

それ以外の文頭ケース:「です」のみが存在、本文から始まる

送信者表示名(SenderName)orメールアドレスから宛先名を判断し、①送信者表示名か②TOメアドを基に置き換えます。

①送信者表示名はTOメアドの表示と異なります。 これは管理者の設定値により表示方法が異なる為です。

プロパティ備考
SenderName表示名( <>は含まれない) To / CCのプロパティも同じ
SenderEmailAddress技術的なメールアドレス(SMTP)

Sendernameプロパティは会社によって設定が変わります。今回は「英語名前(日本語名前)」というメアド設定されている前提にしています。つまり「Suzuki Taro(鈴木太郎).com」のような表記

SenderEmailAddressはそのまま使用すると、メアド以外の情報も含めてまとめて取得される場合があります。「/O=EXCHANGELABS/OU=EXCHANGE ADMINISTRATIVE GROUP …」のような値です。

上記のような値が取得される場合、ExchangeUserを経由してメアド取得する必要があります。そこでGetSMTPAddress関数で、通常のSMTPかどうか判定しメアド取得しています。

これにより社内exchangeシステムでの取得値で、外部メールでもメアドだけを取得可能です。

②TOメアドを元に宛先とする場合、複数の宛先が入ってしまい送信者を確実に特定できない場合もあります。

極力TOメアドで置き換えを避けるため、本文にあなたの和英苗字が入っていれば、そのメール送信者を返信対象にすると解釈し、メール表示名を宛先に置き換えます。

これはFunction fExtractNameInMail関数の第4、5引数 :YouLstName As StringとYouLstNameJp As Stringに指定した値が使用されます。今回は静的な苗字を設定していますが、シート上の値を格納するなどして動的に動作するようにしてください。

それでもToメアドから宛先を判定する場合、下記関数の元で、和英どちらかの苗字か、メールから名前全体を取得します。

第3引数でjaかenを指定することで和文混合メアドの場合に意図した苗字を抜き出す

Tanaka Taro (田中 太郎)@abc.comのようなメアド標識の場合、引数と括弧を元にTanakaあるいは田中が抽出されます。

カッコが無いのメアド形式の場合は、@までを取得してメアドでの名前を取得します。英語のみの場合、一文字目は大文字として取得します。

また、Toメアドで宛先抽出した際に、和文混合ではないため英語名のみ取得できる場合があります。その際は下記部分TranslateTects_DefJPandENで翻訳可能です。

翻訳関数ではGAS,GoogleCloud翻訳API,AzureAPIどれかを使用可能です。翻訳関数は下記記事で紹介しています。

・応用:動的メール作成

今マクロを活用することで、選択したメールに応じて宛先を置き換えたメール作成が可能です。さらに、キーワードによる置き換えや自動翻訳機能を付けることで、ただのテンプレメール作成にとどまらない、柔軟なメール作成が即座に可能になります。

-業務効率化