从引用的回复中解析电子邮件内容

时间:2008-11-10 18:54:30

标签: c# ruby email email-parsing

我正在尝试弄清楚如何从可能包含的任何引用的回复文本中解析出电子邮件的文本。我已经注意到,通常电子邮件客户端会将“在某个日期等等上写下来”或者用尖括号作为前缀。不幸的是,不是每个人都这样做有没有人知道如何以编程方式检测回复文本?我正在使用C#来编写这个解析器。

10 个答案:

答案 0 :(得分:58)

我在这方面做了很多搜索,这是我发现的。基本上有两种情况可以解决这个问题:当你有整个线程时,你什么时候没有。我将其分为两类:

当你有线程时:

如果您拥有整个系列的电子邮件,则可以非常高度地确保您要移除的内容实际上是引用的文本。有两种方法可以做到这一点。一,您可以使用消息的Message-ID,In-Reply-To ID和Thread-Index来确定单个消息,它的父节点以及它所属的线程。有关详细信息,请参阅RFC822RFC2822this interesting article on threadingthis article on threading。一旦你重新组装了线程,你就可以删除外部文本(例如To,From,CC等等......),你就完成了。

如果您使用的邮件没有标题,您还可以使用相似性匹配来确定电子邮件的哪些部分是回复文本。在这种情况下,您仍然坚持进行相似性匹配以确定重复的文本。在这种情况下,您可能需要查看Levenshtein Distance algorithm,例如this one on Code Projectthis one

无论如何,如果您对线程处理感兴趣,请查看this great PDF on reassembling email threads

当你没有线程时:

如果您只遇到来自该主题的一条消息,那么您必须尝试猜测该引用是什么。在这种情况下,我看到的是不同的报价方法:

  1. 一条线(如展望中所示)。
  2. 角度支架
  3. “---原始讯息---”
  4. “就在某一天,某某写道:”
  5. 从那里删除文本,你就完成了。其中任何一个的缺点是,他们都假设发件人将他们的回复置于引用文本之上并且没有交错它(就像互联网上的旧样式一样)。如果发生这种情况,祝你好运。我希望这可以帮助你们中的一些人!

答案 1 :(得分:28)

首先,这是一项棘手的任务。

您应该从不同的电子邮件客户端收集典型的响应,并准备正确的正则表达式(或其他)来解析它们。我收集了来自outlook,thunderbird,gmail,apple mail和mail.ru的回复。

我使用正则表达式以下列方式解析响应:如果表达式不匹配,我尝试使用下一个。

new Regex("From:\\s*" + Regex.Escape(_mail), RegexOptions.IgnoreCase);
new Regex("<" + Regex.Escape(_mail) + ">", RegexOptions.IgnoreCase);
new Regex(Regex.Escape(_mail) + "\\s+wrote:", RegexOptions.IgnoreCase);
new Regex("\\n.*On.*(\\r\\n)?wrote:\\r\\n", RegexOptions.IgnoreCase | RegexOptions.Multiline);
new Regex("-+original\\s+message-+\\s*$", RegexOptions.IgnoreCase);
new Regex("from:\\s*$", RegexOptions.IgnoreCase);

最后删除引用:

new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline);

以下是我的一小部分测试答案(样本除以 --- ):

From: test@test.com [mailto:test@test.com] 
Sent: Tuesday, January 13, 2009 1:27 PM
----
2008/12/26 <test@test.com>

>  text
----
test@test.com wrote:
> text
----
      test@test.com wrote:         text
text
----
2009/1/13 <test@test.com>

>  text
----
 test@test.com wrote:         text
 text
----
2009/1/13 <test@test.com>

> text
> text
----
2009/1/13 <test@test.com>

> text
> text
----
test@test.com wrote:
> text
> text
<response here>
----
--- On Fri, 23/1/09, test@test.com <test@test.com> wrote:

> text
> text

致敬,Oleg Yaroshevych

答案 2 :(得分:23)

谢谢Goleg的正则表达!真的很有帮助。这不是C#,但对于那里的googlers,这是我的Ruby解析脚本:

def extract_reply(text, address)
    regex_arr = [
      Regexp.new("From:\s*" + Regexp.escape(address), Regexp::IGNORECASE),
      Regexp.new("<" + Regexp.escape(address) + ">", Regexp::IGNORECASE),
      Regexp.new(Regexp.escape(address) + "\s+wrote:", Regexp::IGNORECASE),
      Regexp.new("^.*On.*(\n)?wrote:$", Regexp::IGNORECASE),
      Regexp.new("-+original\s+message-+\s*$", Regexp::IGNORECASE),
      Regexp.new("from:\s*$", Regexp::IGNORECASE)
    ]

    text_length = text.length
    #calculates the matching regex closest to top of page
    index = regex_arr.inject(text_length) do |min, regex|
        [(text.index(regex) || text_length), min].min
    end

    text[0, index].strip
end

到目前为止它运作良好。

答案 3 :(得分:11)

到目前为止,最简单的方法是在内容中放置一个标记,例如:

---请回答以上这一行---

毫无疑问,由于不同的电子邮件客户端以不同的方式引用文本,因此解析引用的文本并非易事。要正确解决此问题,您需要在每个电子邮件客户端中进行考虑和测试。

Facebook可以做到这一点,但除非你的项目有很大的预算,否则你可能不会。

Oleg用正则表达式解决了这个问题,找到了“2012年7月13日,13:09,xxx写道:”文字。但是,如果用户删除此文本,或者在电子邮件底部回复,就像许多人一样,此解决方案将无效。

同样,如果电子邮件客户端使用不同的日期字符串,或者不包含日期字符串,则正则表达式将失败。

答案 4 :(得分:6)

电子邮件中没有回复的通用指示符。您可以做的最好的事情是尝试捕捉最常见的并解析新模式。

请记住,有些人在引用的文本中插入回复(例如,我的老板在我问他们的同一行回答问题)所以不管你做什么,你可能会丢失一些你想保留的信息。

答案 5 :(得分:6)

这是@ hurshagrawal的Ruby代码的C#版本。我不太了解Ruby,所以它可能会关闭,但我认为我做对了。

public string ExtractReply(string text, string address)
{
    var regexes = new List<Regex>() { new Regex("From:\\s*" + Regex.Escape(address), RegexOptions.IgnoreCase),
                        new Regex("<" + Regex.Escape(address) + ">", RegexOptions.IgnoreCase),
                        new Regex(Regex.Escape(address) + "\\s+wrote:", RegexOptions.IgnoreCase),
                        new Regex("\\n.*On.*(\\r\\n)?wrote:\\r\\n", RegexOptions.IgnoreCase | RegexOptions.Multiline),
                        new Regex("-+original\\s+message-+\\s*$", RegexOptions.IgnoreCase),
                        new Regex("from:\\s*$", RegexOptions.IgnoreCase),
                        new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                    };

    var index = text.Length;

    foreach(var regex in regexes){
        var match = regex.Match(text);

        if(match.Success && match.Index < index)
            index = match.Index;
    }

    return text.Substring(0, index).Trim();
}

答案 6 :(得分:3)

如果您控制原始邮件(例如来自网络应用程序的通知),您可以放置​​一个独特的,可识别的标头,并将其用作原始邮件的分隔符。

答案 7 :(得分:1)

这是一个很好的解决方案。搜索了这么久后找到了它。

如上所述,这是一个例外情况,因此上述表达式没有正确解析我的gmail和outlook(2010)响应,为此我添加了以下两个正则表达式。如有任何问题,请告诉我。

//Works for Gmail
new Regex("\\n.*On.*<(\\r\\n)?" + Regex.Escape(address) + "(\\r\\n)?>", RegexOptions.IgnoreCase),
//Works for Outlook 2010
new Regex("From:.*" + Regex.Escape(address), RegexOptions.IgnoreCase),

干杯

答案 8 :(得分:0)

这是旧帖子,但是,不确定你是否知道github有a Ruby lib提取回复。如果你使用.NET,我在https://github.com/EricJWHuang/EmailReplyParser

有一个.NET

答案 9 :(得分:0)

如果您使用SigParser.com的API,它会从单个电子邮件文本字符串中为您提供回复链中所有已分发电子邮件的数组。因此,如果有10封电子邮件,您将获得所有10封电子邮件的文本。

enter image description here

您可以在此处查看详细的API规范。

https://api.sigparser.com/

enter image description here