繁体   English   中英

如何从电子邮件正文中复制特定文本?

[英]How to copy specific text from the body of the email?

Option Explicit

Sub GetFromInbox()

Dim olApp As Outlook.Application
Dim olNs As Outlook.Namespace
Dim olFldr As Outlook.MAPIFolder
Dim olItms As Outlook.Items
Dim olMail As Variant
Dim i As Long

Set olApp = New Outlook.Application
Set olNs = olApp.GetNamespace("MAPI")
Set olFldr = olNs.GetDefaultFolder(olFolderInbox).Folders("impMail")
Set olItms = olFldr.Items

olItms.Sort "Subject"

For Each olMail In olItms
    If InStr(olMail.Subject, "SubjectoftheEmail") > 0 Then
        ThisWorkbook.Sheets("Fixings").Cells(2, 2).Value = olMail.Body

    End If
Next olMail

Set olFldr = Nothing
Set olNs = Nothing
Set olApp = Nothing
End Sub

此代码帮助我下载电子邮件的整个正文,但我需要在单元格中使用特定的粗体文本。 电子邮件正文始终如下。 这些行总是以相同的顺序排列。 所有行始终存在。 电子邮件中的所有名称都可以提前知道。

此电子邮件仅供内部使用

你好

@ABC4:请在系统中添加以下详细信息( 2019 年 1 月 12 日):

12345_ABC_MakOpt --- 264532154.78
12345_ABC_GAPFee --- 145626547.80

谢谢

´ ------------------------------------------------- ---- '获取设置'------------------------------------------- -----------

    Dim wb As Workbook
    Dim rngEmailSubject As Range
    Dim rngInstrumentName As Range
    Dim rngDate As Range
    Dim rngAmount As Range
    Dim arrFixing() As typFixing
    Dim rngValue As Range

    Dim rowIdx As Integer
    Dim ix As Integer
    Dim fixingDate As Date

    With wb.Sheets("FixingFromEmail")

        Set rngInstrumentName = .Range("instrument.name")
        Set rngDate = .Range("Date")
        Set rngAmount = .Range("Amount")

        rowIdx = rngInstrumentName.Row
        ix = 0

        Do While True

            rowIdx = rowIdx + 1
             If Not IsEmpty(.Cells(rowIdx, rngInstrumentName.Column).Value) _
        Then

                ix = ix + 1

                ReDim Preserve arrFixing(1 To ix)
                arrFixing(ix).InstrumentName = .Cells(rowIdx, rngInstrumentName.Column).Value
                arrFixing(ix).Date = .Cells(rowIdx, rngDate.Column).Value
                arrFixing(ix).Amount = .Cells(rowIdx, rngAmount.Column).Value


            Else
                Exit Do
            End If

        Loop

    End With´

如果你总是在第一行有一个日期,那么你可以用这样的简单方法得到它:[0-9]{2}-[A-Za-z]{3}-[0-9]{4}

在 regex101 上试试这个,看看 regex 的各个部分做了什么

对于另一部分,我想最简单的方法是阅读整行

您的问题太含糊,无法给出具体答案。 我所能提供的只是第一阶段的一些指导。

您需要决定什么是固定的,什么是可变的。

“@ABC4”是固定的吗? “@ABC4:请在系统中添加以下详细信息(for”是否已修复?

总是有两条数据线吗? 是否有多个数据线,这些是示例? 是这些行的格式:

Xxxxxxx space hyphen hyphen hyphen space amount 

我首先将文本正文分成几行。 几乎可以肯定,这些行被回车换行符打破了。 去测试:

Dim Count As Long

For Each olMail In olItms

  Debug.Print Replace(Replace(Mid$(olMailBody, 1, 200), vbCr, "{c}"), vbLf, "{l}" & vbLf)
  Count = Count + 1
  If Count >= 10 Then
    Exit For
  End If

Next olMail

输出将类似于以下十个(最大)副本:

@ABC4: please add the following detail in system (for 12-Jan-2019):{c}{l}
{c}{l}
12345_ABC_MakOpt --- 264532154.78{c}{l}
12345_ABC_GAPFee --- 145626547.80{c}{l}
Are the characters between lines “{c}{l}” or “{l}” or something else?

在下面的代码中,如有必要,替换vbCR & vbLf然后运行它:

Dim Count As Long
Dim InxL As Long
Dim Lines() As String

For Each olMail In olItms

  Lines = Split(olMail.Body, vbCR & vbLf)
  For InxL = 0 to UBound(Lines)
    Debug.Print InxL + 1 & "  " & Lines(InxL)
  Next
  Count = Count + 1
  If Count >= 10 Then
    Exit For
  End If

Next

输出将类似于以下十个(最多)副本:

0  
1  @ABC4: please add the following detail in system (for 12-Jan-2019):
2  
3  12345_ABC_MakOpt --- 264532154.78
4  12345_ABC_GAPFee --- 145626547.80
5 

现在您可以将文本正文视为线条。 注意:第一行是数字 0。顶部从来没有空行吗? 顶部总是有一个空行吗? 它有所不同吗? 我将假设顶部总是有一个空行。 如果该假设不正确,则需要修改以下代码。

如果第 1 行是“xxxxxxxxxx 日期):”您可以提取日期,以便:

Dim DateCrnt As Date
Dim Pos As Long

DateCrnt = CDate(Left$(Right$(Lines(1), 13), 11))

或者

Pos = InStr(1, Lines(1), "(for ")
DateCrnt = CDate(Mid$(Lines(1), Pos + 5, 11))

注意:这两种方法都取决于您在示例中显示的行尾。 如果有任何变化,您将需要处理该变化的代码。

您现在可以使用如下代码拆分数据行:

Dim NameCrnt As String
Dim AmtCrnt As Double

For InxL = 3 To UBound(Lines)
  If Lines(InxL) <> "" Then
    Pos = InStr(1, Lines(InxL), " --- ")
    If Pos = 0 Then
      Debug.Assert False   ' Line not formatted as expected
    Else
      NameCrnt = Mid$(Lines(InxL), 1, Pos - 1)
      AmtCrnt = Mid$(Lines(InxL), Pos + 5)
    End If
    Debug.Print "Date="& DateCrnt & "    " & "Name=" & NameCrnt & "   " & "Amount=" & AmtCrnt
  End If
Next

输出是:

Date=12/01/2019    Name=12345_ABC_MakOpt   Amount=264532154.78
Date=12/01/2019    Name=12345_ABC_GAPFee   Amount=145626547.8

新部分显示如何将数据从电子邮件添加到工作表

这是本节的第二个版本,因为 OP 改变了他们对所需格式的看法。

下面的代码已经过测试,但我创建的假电子邮件看起来像你问题中的那个。 因此,可能需要进行一些调试。

我创建了一个新工作簿和一个名为“Fixings”的新工作表,标题如下:

宏运行前的空工作表

处理完我的假邮件后,工作表看起来像:

运行后的工作表以添加来自三封每日电子邮件的数据

行的顺序取决于找到电子邮件的顺序。 您可能首先想要最新的。 对工作表进行排序超出了本答案的范围。 注意:列标题告诉宏要记录哪些值。 如果在电子邮件中添加了新行,请添加新的列标题,该值将在不更改宏的情况下保存。

除了一个例外,我不会解释我使用过的 VBA 语句,因为在网上搜索“VBA xxxxx”并找到语句 xxxxx 的规范很容易。 例外是使用两个集合来保存挂起的数据。 其余的解释描述了我的方法背后的原因。

要求会有所变化,但可能不会持续六个月或十二个月。 例如,经理需要不同的标题或不同顺序的列。 您无法预测需要进行哪些更改,但您可以为更改做好准备。 例如,在我的代码顶部,我有:

Const ColFixDate As Long = 1
Const ColFixDataFirst As Long = 2
Const RowFixHead As Long = 1
Const RowFixDataFirst As Long = 2

我可以写Cells(Row, 1).Value = Date 这有两个缺点:(1) 如果日期列曾经被移动过,您必须在代码中搜索访问它的语句;(2) 您必须记住第 1、2 或 3 列中的内容,从而使您的代码更难读。 我避免对行号或列号使用文字。 输入 ColFixDataFirst 而不是 2 的额外努力很快就会得到回报。

我注意到在添加到您的问题的代码中,您使用命名范围来实现相同的效果。 VBA 的一个问题是通常有多种方法可以实现相同的效果。 我更喜欢常量,但我们每个人都必须选择自己的最爱。

我曾在一个处理许多电子邮件和工作簿的部门工作,这些电子邮件和工作簿从外部收到,包含有用的数据,我可以告诉你,它们的格式一直在变化。 将有一个额外的空行或现有的将被删除。 会有额外的数据或现有数据的顺序不同。 作者进行了他们认为有用的更改,但很少做任何有用的事情,例如询问接收者是否想要更改,甚至警告他们更改。 我见过的最糟糕的情况是两个数字列颠倒了,几个月都没有注意到。 幸运的是,我没有参与,因为从我们的系统中备份错误数据然后导入正确的数据是一场噩梦。 我会检查我能想到的所有内容,并拒绝处理与我预期不完全相同的电子邮件。 错误信息全部写入即时窗口,方便开发。 您可能希望使用 MsgBox 或将它们写入文件。 如果邮件处理成功,则不会删除; 它被移动到一个子文件夹,以便在再次需要时可以检索它。

olMail是 Outlook 常量。 不要使用olMail或任何其他保留字作为变量名。

我使用了Session而不是 NameSpace。 它们应该是等效的,但我曾经遇到过无法诊断的 NameSpace 问题,因此我不再使用它们。

我不会对电子邮件进行排序,因为您的代码没有利用对电子邮件进行排序的优势。 也许您可以利用 ReceivedTime 排序,但我可以看到不容易避免的潜在问题。

我以相反的顺序处理电子邮件,因为它们是按位置访问的。 例如,如果将电子邮件 5 移动到另一个文件夹,则之前的电子邮件 6 现在是电子邮件 5,并且For循环将跳过它。 如果以相反的顺序处理电子邮件,您不会介意电子邮件 6 现在是电子邮件 5,因为您已经处理了该电子邮件。

如果您没有设置保存日期或金额的单元格的NumberFormat ,它们将根据 Microsoft 针对您所在国家/地区的默认设置进行显示。 我使用了我最喜欢的显示格式。 换成你最喜欢的。

在处理完整个电子邮件并提取所需数据之前,代码不会向工作表输出任何内容。 这意味着必须存储来自早期数据行的数据,直到处理完所有行。 我用了两个CollectionsPendingNamesPendingAmts 这不是我将数据存储在我为自己编写的宏中的方式。 我的问题是替代方法更复杂或需要更高级的 VBA。

回来就你不明白的任何其他问题提出问题。

Option Explicit
Sub GetFromInbox()

  Const ColFixDate As Long = 1
  Const ColFixName As Long = 2
  Const ColFixAmt As Long = 3
  Const RowFixDataFirst As Long = 2

  Dim AmtCrnt As Double
  Dim ColFixCrnt As Long
  Dim DateCrnt As Date
  Dim ErrorOnEmail As Boolean
  Dim Found As Boolean
  Dim InxItem As Long
  Dim InxLine As Long
  Dim InxPend As Long
  Dim Lines() As String
  Dim NameCrnt As String
  Dim olApp As New Outlook.Application
  Dim olFldrIn As Outlook.Folder
  Dim olFldrOut As Outlook.Folder
  Dim olMailCrnt As Outlook.MailItem
  Dim PendingAmts As Collection
  Dim PendingNames As Collection
  Dim Pos As Long
  Dim RowFixCrnt As Long
  Dim StateEmail As Long
  Dim TempStg As String
  Dim WshtFix As Worksheet

  Set WshtFix = ThisWorkbook.Worksheets("Fixings")
  With WshtFix
    RowFixCrnt = .Cells(Rows.Count, ColFixDate).End(xlUp).Row + 1
  End With

  Set olApp = New Outlook.Application
  Set olFldrIn = olApp.Session.GetDefaultFolder(olFolderInbox).Folders("impMail")
  Set olFldrOut = olFldrIn.Folders("Processed")

  For InxItem = olFldrIn.Items.Count To 1 Step -1

    If olFldrIn.Items(InxItem).Class = Outlook.olMail Then

      Set olMailCrnt = olFldrIn.Items(InxItem)

      If InStr(olMailCrnt.Subject, "SubjectoftheEmail") > 0 Then
        Lines = Split(olMailCrnt.Body, vbCr & vbLf)

        'For InxLine = 0 To UBound(Lines)
        '  Debug.Print InxLine + 1 & "  " & Lines(InxLine)
        'Next

        StateEmail = 0    ' Before "please add ..." line
        ErrorOnEmail = False
        Set PendingAmts = Nothing
        Set PendingNames = Nothing
        Set PendingAmts = New Collection
        Set PendingNames = New Collection

        For InxLine = 0 To UBound(Lines)
          NameCrnt = ""     ' Line is not a data line
          Lines(InxLine) = Trim(Lines(InxLine))  ' Remove any leading or trailing spaces

          ' Extract data from line
          If Lines(InxLine) <> "" Then
            If StateEmail = 0 Then
              If InStr(1, Lines(InxLine), "please add the ") = 0 Then
                Debug.Print "Error with email received " & olMailCrnt.ReceivedTime & vbLf & _
                            "  The first non-blank line is" & vbLf & _
                            "    " & Lines(InxLine) & vbLf & _
                            "  but I was expecting something like:" & vbLf & _
                            "    @ABC4: please add the following detail in system (for 13-Jan-2019):"
                ErrorOnEmail = True
                Exit For
              End If
              TempStg = Left$(Right$(Lines(InxLine), 13), 11)
              If Not IsDate(TempStg) Then
                Debug.Print "Error with email received " & olMailCrnt.ReceivedTime & vbLf & _
                            "  The value I extracted from the ""please add the ...""" & _
                            " line is """ & vbLf & "  " & TempStg & _
                            """ which I do not recognise as a date"
                ErrorOnEmail = True
                Exit For
              End If
              DateCrnt = CDate(TempStg)
              StateEmail = 1    ' After "please add ..." line
            ElseIf StateEmail = 1 Then
              If Lines(InxLine) = "" Then
                ' Ignore blank line
              ElseIf Lines(InxLine) = "thanks" Then
                ' No more data lines
                Exit For
              Else
                Pos = InStr(1, Lines(InxLine), " --- ")
                If Pos = 0 Then
                  Debug.Print "Error with email received " & olMailCrnt.ReceivedTime & vbLf & _
                              "  Data line: " & Lines(InxLine) & vbLf & _
                              "    does not contain ""---"" as required"
                  ErrorOnEmail = True
                  'Debug.Assert False
                  Exit For
                End If
                NameCrnt = Mid$(Lines(InxLine), 1, Pos - 1)
                TempStg = Mid$(Lines(InxLine), Pos + 5)
                If Not IsNumeric(TempStg) Then
                  Debug.Print "Error with email received " & olMailCrnt.ReceivedTime & vbLf & _
                              "  Data line:" & Lines(InxLine) & vbLf & _
                              "    value after ""---"" is not an amount"
                  ErrorOnEmail = True
                  'Debug.Assert False
                  Exit For
                End If
                AmtCrnt = CDbl(TempStg)
              End If
            End If  ' StateEmail
          End If ' Lines(InxLine) <> ""

          If ErrorOnEmail Then
            ' Ignore any remaining lines
            Exit For
          End If

          If NameCrnt <> "" Then
            ' Line was a data line without errors. Save until know entire email is error free
            PendingNames.Add NameCrnt
            PendingAmts.Add AmtCrnt
          End If

        Next InxLine

        If Not ErrorOnEmail Then
          ' Output pending rows now know entire email is error-free
          With WshtFix
            For InxPend = 1 To PendingNames.Count
              With .Cells(RowFixCrnt, ColFixDate)
                .Value = DateCrnt
                .NumberFormat = "d mmm yy"
              End With
              .Cells(RowFixCrnt, ColFixName).Value = PendingNames(InxPend)
              With .Cells(RowFixCrnt, ColFixAmt)
                .Value = PendingAmts(InxPend)
                .NumberFormat = "#,##0.00"
              End With
              RowFixCrnt = RowFixCrnt + 1
            Next
          End With
          ' Move fully processed email to folder Processed
          olMailCrnt.Move olFldrOut
        End If

      End If  ' InStr(olMailCrnt.Subject, "SubjectoftheEmail") > 0
    End If  ' olFldrIn.Items(InxItem).Class = Outlook.olMail

  Next InxItem

  Set olFldrIn = Nothing
  Set olFldrOut = Nothing
  olApp.Quit
  Set olApp = Nothing

End Sub

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM