从引用回复中parsing电子邮件内容

我试图弄清楚如何从任何可能包含的引用回复文本中parsing出电子邮件的文本。 我已经注意到,通常电子邮件客户端会在“如此如此写的date”上放置一个“尖括号”。 不幸的是,不是每个人都这样做 有没有人有任何想法如何以编程方式检测答复文本? 我正在使用C#编写这个parsing器。

我做了很多更多的search,这是我find的。 基本上有两种情况:你有整个线程,什么时候没有。 我将把它分成两类:

当你有线程:

如果你有整个系列的电子邮件,你可以达到很高的保证,你正在删除的是实际引用的文本。 有两种方法可以做到这一点。 一个,你可以使用消息的Message-ID,In-Reply-To ID和Thread-Index来确定单个消息,它的父代以及它所属的线程。 有关这方面的更多信息,请参阅RFC822 , RFC2822 , 关于线程的这篇有趣的文章 ,或者关于线程的本文 。 一旦你重新组装了线程,你就可以删除外部文本(比如To,From,CC等等),你就完成了。

如果您使用的邮件没有标题,则还可以使用相似性匹配来确定电子邮件的哪些部分是答复文本。 在这种情况下,你坚持做相似性匹配来确定重复的文本。 在这种情况下,您可能需要查看一个Levenshtein距离algorithm,例如Code Project上的这个 algorithm 。

无论如何,如果您对线程处理感兴趣,请查看有关重新组装电子邮件线程的优秀PDF 。

当你没有线程时:

如果你只从线程中获得一条消息,你就不得不猜测报价是什么。 在这种情况下,我看到了不同的引用方法:

  1. 一条线(如前所见)。
  2. 尖括号
  3. “ – -原始信息 – -”
  4. “在某某天,某某写道:”

从那里删除文本,你就完成了。 其中任何一个缺点是,他们都假设发件人把他们的回复放在引用的文本之上,而不是交错(就像互联网上的旧风格)。 如果发生这种情况,祝你好运。 我希望这可以帮助你们中的一些人!

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

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

我正在使用正则expression式来parsing以下方式的响应:如果expression式不匹配,我尝试使用下一个。

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); 

这里是我收集的testing回答(样本除以 ):

 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 

最好的问候,奥列格Yaroshevych

谢谢,Goleg,正则expression式! 真的帮了忙。 这不是C#,但对于那里的谷歌,这是我的Rubyparsing脚本:

 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 

到目前为止,它工作得很好。

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

—请回复上面这一行—

正如你毫无疑问注意到的,parsing引用文本并不是一件容易的事情,因为不同的电子邮件客户以不同的方式引用文本。 要正确地解决这个问题,您需要在每个电子邮件客户端中进行考虑和testing。

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

奥列格已经解决了这个问题,使用正则expression式find“2012年7月13日,13:09,xxx写道:”文字。 但是,如果用户删除了这个文本,或者在电子邮件的底部回复,那么这个解决scheme将无法工作。

同样,如果电子邮件客户端使用不同的datestring,或者不包含datestring,则正则expression式将失败。

电子邮件中没有回复的通用指示符。 你可以做的最好的尝试是赶上最常见的parsing新模式,当你遇到他们。

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

这是@ hurshagrawal的Ruby代码的C#版本。 我不太了解Ruby,所以可能会closures,但我想我是对的。

 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(); } 

如果您控制原始消息(例如来自Web应用程序的通知),则可以放置一个明确的,可识别的标题,并将其用作原始文章的分隔符。

这是一个很好的解决scheme。 找了这么久才find的

另外,如上所述,这是明智的,所以上述expression式没有正确parsing我的gmail和outlook(2010)响应,为此我添加了以下两个正则expression式。 让我知道任何问题。

 //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), 

干杯

这是旧的post,但是,不知道你是否知道github有一个Ruby库提取答复。 如果你使用.NET,我有一个.NET在https://github.com/EricJWHuang/EmailReplyParser