0

我正在尝试扩展某些 Outlook 电子邮件报废 VBA 代码的功能。我会定期收到退回的电子邮件,并希望通过将所述电子邮件地址导出到 MS Excel 来跟踪这些(删除)。

代码在一定程度上有效。我只能使用 RegEx 抓取典型的退回通知电子邮件中的第一个电子邮件地址。我工作的公司的邮件服务器将来自同一域的电子邮件聚合到一封通知电子邮件中。因此,我收到了多封包含多封退回电子邮件的通知电子邮件。

如何让 RegEx 循环浏览整个通知电子邮件以收集所有电子邮件地址???我现在有点卡住了,因为——不可否认——我对 RegEx 了解不多,并且“采用”了这段代码的大部分......

感谢您对 Stackoverflow 的帮助!!!

Sub Extract_Invalid_To_Excel()

Dim olApp As Outlook.Application
Dim olExp As Outlook.Explorer
Dim olFolder As Outlook.MAPIFolder
Dim obj As Object
Dim stremBody As String
Dim stremSubject As String
Dim i As Long
Dim x As Long
Dim count As Long
Dim RegEx As Object
Set RegEx = CreateObject("VBScript.RegExp")
Dim xlApp As Object 'Excel.Application
Dim xlwkbk As Object 'Excel.Workbook
Dim xlwksht As Object 'Excel.Worksheet
Dim xlRng As Object 'Excel.Range

Set olApp = Outlook.Application
Set olExp = olApp.ActiveExplorer

Set olFolder = olExp.CurrentFolder

'Open Excel
Set xlApp = GetExcelApp
xlApp.Visible = True
If xlApp Is Nothing Then GoTo ExitProc

Set xlwkbk = xlApp.Workbooks.Add
Set xlwksht = xlwkbk.Sheets(1)
Set xlRng = xlwksht.Range("A1")
xlRng.Value = "Bounced email addresses"

'Set count of email objects
count = olFolder.Items.count

'counter for excel sheet
i = 0
'counter for emails
x = 1

For Each obj In olFolder.Items '**Loops through selected Outlook folder**
    xlApp.StatusBar = x & " of " & count & " emails completed"
    stremBody = obj.Body
    stremSubject = obj.Subject

    If checkEmail(stremBody) = True Then '**Checks email for keywords in email
        'MsgBox ("finding email: " & stremBody)

        '**RegEx to find email addresses within message body
        With RegEx
            .Pattern = "\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b"
            .IgnoreCase = True
            .MultiLine = True
            .Global = False
            Set olMatches = .Execute(stremBody) 'Executes RegEx function

            'Loop through RegEx matches
            For Each match In olMatches
                xlwksht.Cells(i + 2, 1).Value = match
                i = i + 1
            Next match
        End With
        'TODO: move or mark the email that had the address extracted
    Else
        '**To view the items that aren't being parsed uncomment the following line
        'MsgBox (stremBody)
    End If

    x = x + 1
Next obj
xlApp.ScreenUpdating = True
MsgBox ("Invalid Email addresses are done being extracted")

ExitProc:
Set xlRng = Nothing
Set xlwksht = Nothing
Set xlwkbk = Nothing
Set xlApp = Nothing
Set emItm = Nothing
Set olFolder = Nothing
Set olNS = Nothing
Set olApp = Nothing
End Sub

Function GetExcelApp() As Object
' always create new instance
On Error Resume Next
Set GetExcelApp = CreateObject("Excel.Application")
On Error GoTo 0
End Function

Function checkEmail(ByVal Body As String) As Boolean
    Dim keywords(3) As String
    keywords(0) = "recipient's e-mail address was not found"
    keywords(1) = "error occurred while trying to deliver this message"
    keywords(2) = "message wasn't delivered"

    'Default value
    checkEmail = False
     For Each word In keywords
        If InStr(1, Body, word, vbTextCompare) > 1 Then
            checkEmail = True
            Exit For
        End If
     Next word
End Function

提供更多细节。我会收到数百封包含以下文本的电子邮件:

Delivery has failed to these recipients or distribution lists:

John.Doe@abc.com
The recipient's e-mail address was not found in the recipient's e-mail system. Microsoft Exchange will not try to redeliver this message for you. Please check the e-mail address and try resending this message, or provide the following diagnostic text to your system administrator.

Morgan.Freedman@abc.com
The recipient's e-mail address was not found in the recipient's e-mail system. Microsoft Exchange will not try to redeliver this message for you. Please check the e-mail address and try resending this message, or provide the following diagnostic text to your system administrator.

Michael.Jordan@abc.com
The recipient's e-mail address was not found in the recipient's e-mail system. Microsoft Exchange will not try to redeliver this message for you. Please check the e-mail address and try resending this message, or provide the following diagnostic text to your system administrator.

上面的代码能够获取电子邮件正文中的第一个电子邮件地址(即 John.Doe@abc.com),但看不到其他两个电子邮件地址...

其余代码完美无缺。它将找到的电子邮件地址导出到 Excel 中。

4

3 回答 3

2

我收到多达 200 封退回的电子邮件通知,每个大型电子邮件分发。使用 Constant Contact 很容易,因为该工具会将所有退回的地址和代码处理成一个不错的文件。使用 Outlook,我是一个人,但出于其他原因,我更喜欢它。所以我想出了一个程序和 VBA 宏来完成任务。首先,我将所有我希望处理的电子邮件放入一个文件夹并选择它。使用 Outlook 2010,我转到文件 -> 选项 -> 高级 -> 导出。从那里我选择EXPORT TO A FILE (Next),然后选择最后一个选项 TAB SEPARATED VALUES (Windows)。然后,您选择名称和文件夹位置来存储一个 TXT 文件,该文件将文件夹中的所有电子邮件组合在一起。在 Msft Word 中打开文件并运行以下 VBA 宏:

Sub Bounced_Email_Harvester()
'
' Bounced-Email Text-Process Macro
'
Dim flag As Boolean
' DocLen is to maintain Document length in characters
Dim DocLen As Long
' Try to speed up Word by suspending unnecessary tasks
ActiveDocument.ActiveWindow.View.Draft = True
Options.Pagination = False
Options.CheckGrammarAsYouType = False
Options.CheckSpellingAsYouType = False
Application.ScreenUpdating = False
' Remove extraneous bracket characters < & >
    Selection.Find.ClearFormatting
    Selection.Find.Replacement.ClearFormatting
    With Selection.Find
        .Text = ">>>"
        .Replacement.Text = "###"
        .Forward = True
        .Wrap = wdFindContinue
    End With
    Selection.Find.Execute Replace:=wdReplaceAll
    With Selection.Find
        .Text = "<<<"
        .Replacement.Text = "VVV"
        .Forward = True
        .Wrap = wdFindContinue
    End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.WholeStory
DocLen = Len(Selection)
Application.DisplayStatusBar = True
Selection.HomeKey Unit:=wdStory
' CORE OF MACRO IS WITHIN THIS LOOP
Do While DocLen > 800
    ' Selects text until next @ sign is reached - locating email addresses
    flag = True
    While flag = True
        Selection.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdExtend
        DocLen = DocLen - 1
        If Strings.Right(Selection.Range.Text, 1) = "@" Or DocLen < 2 Then flag = False
        Wend
    flag = True
    Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
    DocLen = DocLen + 1
    While flag = True
        Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
        DocLen = DocLen + 1
    'Locate the Beginning of email seeking demarkations (brackets, space, tab, paragraph)
        If Strings.Right(Selection.Range.Text, 1) = "<" Or Strings.Right(Selection.Range.Text, 1) = "[" Or Strings.Right(Selection.Range.Text, 1) = "(" Or Strings.Right(Selection.Range.Text, 1) = " " _
        Or Strings.Right(Selection.Range.Text, 1) = Chr$(9) Or Strings.Right(Selection.Range.Text, 1) = Chr$(13) Or DocLen < 2 Then flag = False
    Wend
    Selection.TypeParagraph
    flag = True
    While flag = True
        Selection.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdExtend
        DocLen = DocLen - 1
'Locate the End of email seeking demarkations (brackets, space, tab, paragraph)
        If Strings.Right(Selection.Range.Text, 1) = ">" Or Strings.Right(Selection.Range.Text, 1) = "]" Or Strings.Right(Selection.Range.Text, 1) = ")" Or Strings.Right(Selection.Range.Text, 1) = " " _
        Or Strings.Right(Selection.Range.Text, 1) = Chr$(9) Or Strings.Right(Selection.Range.Text, 1) = Chr$(13) Or DocLen < 2 Then flag = False
    Wend
    Selection.Collapse Direction:=wdCollapseEnd
    Selection.Previous(Unit:=wdCharacter, Count:=1).Select
    DocLen = DocLen + 1
    Selection.TypeParagraph
Loop
' END OF CORE MACRO LOOP
Selection.Collapse Direction:=wdCollapseEnd
    Selection.Previous(Unit:=wdCharacter, Count:=1).Select
    Selection.TypeParagraph
' Major work done - now some pesky house cleaning....
Selection.Find.ClearFormatting
With Selection.Find
    .Text = "mailto:"
    .Replacement.Text = "^p"
    .Forward = True
    .Wrap = wdFindContinue
End With
Selection.Find.Execute Replace:=wdReplaceAll
With Selection.Find
    .Text = ":550^p"
    .Replacement.Text = "^p"
    .Forward = True
    .Wrap = wdFindContinue
End With
Selection.Find.Execute Replace:=wdReplaceAll
With Selection.Find
    .Text = "<^p"
    .Replacement.Text = "^p"
    .Forward = True
    .Wrap = wdFindContinue
End With
Selection.Find.Execute Replace:=wdReplaceAll
    With Selection.Find
    .Text = ";^p"
    .Replacement.Text = "^p"
    .Forward = True
    .Wrap = wdFindContinue
End With
Selection.Find.Execute Replace:=wdReplaceAll
With Selection.Find
    .Text = "...^p"
    .Replacement.Text = "^p"
    .Forward = True
    .Wrap = wdFindContinue
End With
Selection.Find.Execute Replace:=wdReplaceAll
With Selection.Find
    .Text = ".^p"
    .Replacement.Text = "^p"
    .Forward = True
    .Wrap = wdFindContinue
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.Execute Replace:=wdReplaceAll
    With Selection.Find
    .Text = "^p^p"
    .Replacement.Text = "^p"
    .Forward = True
    .Wrap = wdFindContinue
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.Execute Replace:=wdReplaceAll
MsgBox ("Count: " & DocLen)
End Sub

Word 可能会流失 10 或 15 分钟,并且似乎被锁定了。我点击另一个应用程序,有时会观看任务管理器,以根据性能监视器确认它仍在工作。早上开始后我会去煮咖啡。它最终会以一个提供无意义数字的消息框结束。点击它离开。现在已完成,您将拥有一长列以段落分隔的电子邮件地址。它似乎可以可靠地提取 100% 的电子邮件等等;例如,某些邮件服务器使用电子邮件域的派生来响应,例如同一用户的@us.att.com 和@att.com 或@jpmogan.com 和@jpchase.com。

复制整个内容并放入 Excel 列。从这里对列表进行排序并删除明显的谷壳,前 20% 的电子邮件地址是以数字开头的电子邮件地址,以信封开头的电子邮件地址、标题、邮件、邮局主管、SMTP、X-Sender 和大量重复发送电子邮件地址。然后在其上运行数据透视表以消除所有重复项。您现在可以将您的电子邮件列表导入到您的 dB 中,以标记为退回的电子邮件地址。Word 宏完成后的整个后期处理只需要我 10 到 15 分钟。我可能花费了比需要更多的时间,因为无效的电子邮件地址将被我的 dB 链接简单地忽略。

该宏不提取退回代码,因此您不知道它是软退回(邮箱已满)还是硬退回(未找到收件人)。您可以在将它们放入文件夹之前尝试识别它们,或者您可以采用策略要求随着时间的推移需要两次退回,然后才能永久删除。你的来电。

我应该注意我不是 VBA 程序员。40 年前,我在 Commodore 计算机上学习了 Basic 语言,有时还涉足 Msft Access 的一些功能。我使用 VBA for Word 的大部分经验仅限于录制宏,然后使用自动生成的代码自动执行一些重复性任务。知道自己在做什么的人可能会极大地改进我的代码,但它对我有用已经大大节省了时间。

于 2018-09-21T07:09:51.150 回答
0

虽然对 RegEx 函数还很陌生,但我盲目地稍微修改了代码。

我将 RegEx.Global 布尔值更改为 True,此代码将完美运行。

With RegEx
   yadda yadda yadda
   .Global = True
End With

好吧-无论如何谢谢。希望这对其他人有帮助!!!

于 2014-08-22T14:55:48.760 回答
0

经过多次狩猎,我能够提出以下功能。一些正文仍然包含无效字符(不知道为什么),但总体正确率约为 90%。此函数解析传递的 Outlook 项目集合并将所有唯一的电子邮件地址(在 ReportItem 的正文中找到)添加到字符串列表,该列表最后写入立即窗口。

Private Sub ListEmailAddresses(outlookItems As Outlook.Items)
  Dim folder As Outlook.MAPIFolder = Nothing
  Try
    Dim emailAddresses As New List(Of String)
    If TypeOf outlookItems.Parent Is Outlook.MAPIFolder Then
      folder = CType(outlookItems.Parent, Outlook.MAPIFolder)
    End If
    For i = 1 To outlookItems.Count
      Dim objItem As Object = outlookItems(i)
      Try
        If TypeOf objItem Is Outlook.ReportItem Then
          Dim rpt As Outlook.ReportItem = TryCast(objItem, Outlook.ReportItem)
          Dim temp() As Byte = System.Text.Encoding.Unicode.GetBytes(rpt.Body.ToArray())
          Dim sb As New System.Text.StringBuilder
          For z As Integer = 0 To temp.Length - 1
            sb.Append(Chr(temp(z)))
          Next
          Dim rptBody As String = sb.ToString
          Dim mc As MatchCollection = Regex.Matches(rptBody, _
                     "([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})")
          Dim results(mc.Count - 1) As String
          For x As Integer = 0 To results.Length - 1
            Dim emailAddr As String = ValueIfNull(mc(x).Value, "").ToLower
            If Not String.IsNullOrWhiteSpace(emailAddr) Then
              If Not emailAddresses.Contains(emailAddr) Then
                emailAddresses.Add(emailAddr)
              End If
            End If
          Next
        End If
      Catch ex As Exception
        ' Do Something if you care.
      Finally
        Marshal.ReleaseComObject(objItem)
      End Try
    Next
    emailAddresses.Sort()
    Debug.WriteLine(emailAddresses.ToSeparatedString(Environment.NewLine))
  Catch ex As Exception
    ' Do Something if you care.
  Finally
    If folder IsNot Nothing Then Marshal.ReleaseComObject(folder)
  End Try
End Sub
于 2015-06-01T02:50:28.053 回答