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


86

我试图弄清楚如何从可能包含的所有引用的回复文本中解析出电子邮件的文本。我注意到,通常电子邮件客户会在“某某某某日期写某某某日”或在行前加尖括号。不幸的是,并不是每个人都这样做。有人对如何以编程方式检测回复文本有任何想法吗?我正在使用C#编写此解析器。


2
你有运气吗?我正在寻找做同样的事情。
steve_c

有完整源代码示例的最终解决方案吗?
Kiquenet

Quotequail使用Python做到这一点
philfreo 2014年

任何人都可以为其php版本提供帮助吗?
user4271704

Answers:


60

我对此进行了更多搜索,这是我所发现的。基本上有两种情况在执行此操作:拥有整个线程时和没有线程时。我将其分为两类:

当您拥有线程时:

如果您拥有整个系列的电子邮件,则可以确保您要删除的内容实际上是引用的文本,这可以确保非常高的水平。有两种方法可以做到这一点。一,您可以使用邮件的邮件ID,回复到ID和线程索引来确定单个邮件,它的父邮件和它所属的线程。有关此的更多信息,请参见RFC822RFC2822有关线程的这篇有趣文章有关线程的这篇文章。重新组装线程后,就可以删除外部文本(例如“收件人”,“发件人”,“抄送”等行),操作就完成了。

如果您正在处理的消息没有标题,则还可以使用相似性匹配来确定电子邮件的哪些部分是回复文本。在这种情况下,您必须进行相似度匹配以确定重复的文本。在这种情况下,您可能需要研究Levenshtein距离算法,例如在Code Project上的该算法算法

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

当您没有线程时:

如果您只从线程中收到一条消息,则必须尝试猜测报价是多少。在这种情况下,以下是我所看到的不同的报价方法:

  1. 一条线(从Outlook中看到)。
  2. 尖括号
  3. “ - -原始信息 - -”
  4. “在某某日子,某某某写道:”

从此处删除文本,操作完成。所有这些的缺点是,他们都假设发件人将其回复放在引用的文本之上,并且没有交织(就像互联网上的旧样式一样)。如果发生这种情况,祝您好运。希望对您有所帮助!


32

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

您应该从不同的电子邮件客户端收集典型的响应,并准备正确的正则表达式(或其他任何形式)来解析它们。我收集了来自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


如果我不知道电子邮件地址怎么办?
harsimranb 2015年

@ Shyamal-Parikh不适用于html电子邮件,但是电子邮件中通常还包含纯文本消息
maembe

25

谢谢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

到目前为止效果很好。


1
您应该提出一个红宝石问题,并使用此代码回答它,而不是将其发布到ac#问题上。
Matthieu

6
@Matthieu,它不仅是一个C#问题,而且是一个电子邮件和电子邮件解析问题。我认为完全相关。
特伦特

@Trent:然后应该删除C#标签。
Matthieu'2

7
有趣的是,我通过Google搜索主题(不是语言)找到了这个问题,而我实际上需要在Ruby中实现某些东西。所以,加油!
小子

2
这是迄今为止最好的回应。正则表达式与语言无关。感谢您的发帖
超光彩

11

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

---请在此行上方回复---

毫无疑问,解析带引号的文本并不是一件容易的事,因为不同的电子邮件客户端以不同的方式引用文本。为了正确解决此问题,您需要考虑每个电子邮件客户端并进行测试。

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

Oleg已使用正则表达式解决了该问题,并找到了“ 2012年7月13日,13:09,xxx写道:”文本。但是,如果用户像许多人一样删​​除此文本或在电子邮件底部回复,则此解决方案将不起作用。

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


除非您每次答复都在该行中填写,否则此方法将无法答复。
jpw

1
是的,它有缺点。如果用户删除行字符串上方的答复,则您的答复将失败。我发现了这种情况,并向用户发送直接消息,让他们知道他们的消息失败,并带有通过网络应用程序进行回复的链接。大多数用户似乎可以轻松使用它。
superluminary

这应该是公认的答案。但是,我将添加以下信息:如果删除该行,答案将不会成功。
本尼

@Benni-是的,如果删除该行,它将失败。不幸的是,没有一种在电子邮件客户端之间引用文本的标准方法。在删除该行的情况下,您可以将所有文本视为答复。在这种情况下,我认为不可能有完美的解决方案。
superluminary

@superluminary我的意思是,我将其添加到该行中。所以就像-- Please reply above this line. DO NOT REMOVE IT! --。另外,我的经验是,由于某些电子邮件客户端xxx wrote on <datetime>:在整个引号之前,因此在该行之前添加了一行,因此它并不总是有效。该行可以用正则表达式解析,但是由于电子邮件客户端的不同,它可能使用不同的语言和不同的格式。
本尼

6

电子邮件中没有通用的答复指示。您能做的最好的事情就是尝试捕获最常见的样式并在遇到新样式时对其进行解析。

请记住,有些人在引用的文本中插入答复(例如,我的老板在与我询问的同一行回答问题),因此无论您做什么,都可能会丢失一些想要保留的信息。


gmail可以做到...至少它似乎可以做到。据我所知,原始和回复之间的线程ID不变...
kenny

gmail可能会像其他电子邮件客户端一样添加“>”,但这不是电子邮件的标准,也不是您可以指望的东西
3Doubloons

5

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

3

如果您控制原始消息(例如,来自Web应用程序的通知),则可以放置一个不同的,可识别的标题,并将其用作原始帖子的定界符。


0

这是一个很好的解决方案。搜索了很长时间后才找到它。

如上所述,这是一个明智的选择,因此上述表达式不能正确解析我的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),

干杯


任何人都可以为其php版本提供帮助吗?
user4271704


-1

这是旧文章,但是不确定您是否知道github有一个Ruby lib提取答复。如果您使用.NET,我在https://github.com/EricJWHuang/EmailReplyParser中有一个.NET


1
鼓励链接到外部资源,但是请在链接周围添加上下文,以便您的其他用户会知道它是什么以及为什么在那里。如果目标站点无法访问或永久离线,请始终引用重要链接中最相关的部分。
pableiros

您是否正在更新该图书馆?我来搜索是因为C#库无法正确地从Office 365的Outlook中解析出一封简单的电子邮件。然后,我查看了ruby源代码,发现他们的测试用例中有一个完全相同的测试用例,因此他们清楚地认为应该解析它。
Greg Veres

-1

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

在此处输入图片说明

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

https://api.sigparser.com/

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.