我试图弄清楚如何从可能包含的所有引用的回复文本中解析出电子邮件的文本。我注意到,通常电子邮件客户会在“某某某某日期写某某某日”或在行前加尖括号。不幸的是,并不是每个人都这样做。有人对如何以编程方式检测回复文本有任何想法吗?我正在使用C#编写此解析器。
我试图弄清楚如何从可能包含的所有引用的回复文本中解析出电子邮件的文本。我注意到,通常电子邮件客户会在“某某某某日期写某某某日”或在行前加尖括号。不幸的是,并不是每个人都这样做。有人对如何以编程方式检测回复文本有任何想法吗?我正在使用C#编写此解析器。
Answers:
我对此进行了更多搜索,这是我所发现的。基本上有两种情况在执行此操作:拥有整个线程时和没有线程时。我将其分为两类:
当您拥有线程时:
如果您拥有整个系列的电子邮件,则可以确保您要删除的内容实际上是引用的文本,这可以确保非常高的水平。有两种方法可以做到这一点。一,您可以使用邮件的邮件ID,回复到ID和线程索引来确定单个邮件,它的父邮件和它所属的线程。有关此的更多信息,请参见RFC822,RFC2822,有关线程的这篇有趣文章或有关线程的这篇文章。重新组装线程后,就可以删除外部文本(例如“收件人”,“发件人”,“抄送”等行),操作就完成了。
如果您正在处理的消息没有标题,则还可以使用相似性匹配来确定电子邮件的哪些部分是回复文本。在这种情况下,您必须进行相似度匹配以确定重复的文本。在这种情况下,您可能需要研究Levenshtein距离算法,例如在Code Project上的该算法或该算法。
无论如何,如果您对线程处理过程感兴趣,请查看有关重新组装电子邮件线程的出色PDF。
当您没有线程时:
如果您只从线程中收到一条消息,则必须尝试猜测报价是多少。在这种情况下,以下是我所看到的不同的报价方法:
从此处删除文本,操作完成。所有这些的缺点是,他们都假设发件人将其回复放在引用的文本之上,并且没有交织(就像互联网上的旧样式一样)。如果发生这种情况,祝您好运。希望对您有所帮助!
首先,这是一项棘手的任务。
您应该从不同的电子邮件客户端收集典型的响应,并准备正确的正则表达式(或其他任何形式)来解析它们。我收集了来自Outlook,雷鸟,Gmail,苹果邮件和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
谢谢Goleg提供的正则表达式!真的有帮助。这不是C#,但对于在那里的Google员工,这是我的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
到目前为止效果很好。
到目前为止,最简单的方法是在您的内容中放置一个标记,例如:
---请在此行上方回复---
毫无疑问,解析带引号的文本并不是一件容易的事,因为不同的电子邮件客户端以不同的方式引用文本。为了正确解决此问题,您需要考虑每个电子邮件客户端并进行测试。
Facebook可以做到这一点,但是除非您的项目预算很大,否则您可能做不到。
Oleg已使用正则表达式解决了该问题,并找到了“ 2012年7月13日,13:09,xxx写道:”文本。但是,如果用户像许多人一样删除此文本或在电子邮件底部回复,则此解决方案将不起作用。
同样,如果电子邮件客户端使用其他日期字符串,或者不包含日期字符串,则正则表达式将失败。
-- Please reply above this line. DO NOT REMOVE IT! --
。另外,我的经验是,由于某些电子邮件客户端xxx wrote on <datetime>:
在整个引号之前,因此在该行之前添加了一行,因此它并不总是有效。该行可以用正则表达式解析,但是由于电子邮件客户端的不同,它可能使用不同的语言和不同的格式。
电子邮件中没有通用的答复指示。您能做的最好的事情就是尝试捕获最常见的样式并在遇到新样式时对其进行解析。
请记住,有些人在引用的文本中插入答复(例如,我的老板在与我询问的同一行回答问题),因此无论您做什么,都可能会丢失一些想要保留的信息。
这是@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();
}
这是一个很好的解决方案。搜索了很长时间后才找到它。
如上所述,这是一个明智的选择,因此上述表达式不能正确解析我的gmail和Outlook(2010)响应,为此我添加了以下两个Regex。让我知道任何问题。
//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),
干杯
这是旧文章,但是不确定您是否知道github有一个Ruby lib提取答复。如果您使用.NET,我在https://github.com/EricJWHuang/EmailReplyParser中有一个.NET
如果您使用SigParser.com的API,它将通过单个电子邮件文本字符串为您提供回复链中所有细分电子邮件的数组。因此,如果有10封电子邮件,您将获得所有10封电子邮件的文本。
您可以在此处查看详细的API规范。