我试图弄清楚如何从它可能包含的任何引用的回复文本中解析出电子邮件的文本。我注意到,通常电子邮件客户会写上“在某某日期某某某写”或在行前加上尖括号。不幸的是,并不是每个人都这样做。有谁知道如何以编程方式检测回复文本?我正在使用 C# 编写此解析器。
10 回答
我对此进行了更多搜索,这就是我发现的。基本上有两种情况下你会这样做:当你有整个线程时,当你没有时。我将其分为以下两类:
当你有线程时:
如果您拥有整个系列的电子邮件,则可以高度确保您删除的内容实际上是引用的文本。有两种方法可以做到这一点。一,您可以使用消息的 Message-ID、In-Reply-To ID 和 Thread-Index 来确定单个消息、它的父级以及它所属的线程。有关这方面的更多信息,请参阅RFC822、RFC2822、这篇关于线程的有趣文章或这篇关于线程的文章。重新组装线程后,您可以删除外部文本(例如 To、From、CC 等...行),您就完成了。
如果您正在处理的邮件没有标题,您还可以使用相似性匹配来确定电子邮件的哪些部分是回复文本。在这种情况下,您只能进行相似性匹配以确定重复的文本。在这种情况下,您可能需要查看Levenshtein 距离算法,例如Code Project 上的这个或这个。
无论如何,如果您对线程处理过程感兴趣,请查看这个关于重新组装电子邮件线程的精彩 PDF。
当你没有线程时:
如果您只被线程中的一条消息卡住,那么您将不得不尝试猜测引用是什么。在这种情况下,以下是我见过的不同报价方法:
- 一条线(如前景中所见)。
- 尖括号
- “ - -原始信息 - -”
- “某某某日,某某写道:”
从那里删除文本,你就完成了。其中任何一个的缺点是他们都假设发件人将他们的回复放在引用的文本之上并且没有交错(就像互联网上的旧风格一样)。如果发生这种情况,祝你好运。我希望这对你们中的一些人有帮助!
首先,这是一项棘手的任务。
您应该收集来自不同电子邮件客户端的典型响应,并准备正确的正则表达式(或其他)来解析它们。我收集了来自 Outlook、Thunderbird、Gmail、Apple 邮件和 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
谢谢你,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
到目前为止,它工作得很好。
到目前为止,最简单的方法是在您的内容中放置一个标记,例如:
--- 请在此行上方回复 ---
正如您毫无疑问地注意到的那样,解析引用的文本并非易事,因为不同的电子邮件客户端以不同的方式引用文本。要正确解决此问题,您需要在每个电子邮件客户端中进行说明和测试。
Facebook 可以做到这一点,但除非你的项目有很大的预算,否则你可能做不到。
Oleg 已使用正则表达式解决了该问题,以查找“2012 年 7 月 13 日,13:09,xxx 写道:”文本。但是,如果用户像许多人一样删除此文本或在电子邮件底部回复,则此解决方案将不起作用。
同样,如果电子邮件客户端使用不同的日期字符串,或者不包含日期字符串,则正则表达式将失败。
电子邮件中没有通用的回复指标。您能做的最好的事情就是尝试捕捉最常见的模式并在遇到新模式时对其进行解析。
请记住,有些人会在引用的文本中插入回复(例如,我的老板在我问他们的同一行回答问题)所以无论您做什么,您都可能会丢失一些您希望保留的信息。
这是我的 C# 版本的 @hurshagrawal 的 Ruby 代码。我不太了解 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();
}
如果您控制原始消息(例如来自 Web 应用程序的通知),您可以放置一个独特的、可识别的标题,并将其用作原始帖子的分隔符。
这是一个很好的解决方案。找了这么久才找到。
另外,如上所述,这是明智的,因此上述表达式没有正确解析我的 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),
干杯
这是旧帖子,但是,不确定您是否知道 github 有一个提取回复的 Ruby 库。如果您使用 .NET,我在https://github.com/EricJWHuang/EmailReplyParser有一个 .NET
如果您使用SigParser.com的 API,它将为您提供来自单个电子邮件文本字符串的回复链中的所有中断电子邮件的数组。因此,如果有 10 封电子邮件,您将获得所有 10 封电子邮件的文本。
您可以在此处查看详细的 API 规范。