Разбор содержимого электронной почты из цитируемого ответа - PullRequest
80 голосов
/ 10 ноября 2008

Я пытаюсь выяснить, как отделить текст электронного письма от любого цитируемого текста ответа, который он может содержать. Я заметил, что обычно почтовые клиенты ставят «На такую-то дату и так-то написанное» или ставят префикс в скобках. К сожалению, не все это делают. У кого-нибудь есть идеи о том, как программно определять текст ответа? Я использую C # для написания этого парсера.

Ответы [ 10 ]

58 голосов
/ 11 ноября 2008

Я проделал гораздо больше поиска по этому вопросу, и вот что я нашел. Есть две ситуации, когда вы делаете это: когда у вас есть весь поток и когда у вас его нет. Я разобью его на две категории:

Когда у вас есть нить:

Если у вас есть вся серия электронных писем, вы можете достичь очень высокого уровня уверенности в том, что то, что вы удаляете, на самом деле является цитируемым текстом. Есть два способа сделать это. Во-первых, вы можете использовать Message-ID сообщения, In-Reply-To ID и Thread-Index для определения отдельного сообщения, его родителя и потока, к которому оно относится. Для получения дополнительной информации см. RFC822 , RFC2822 , , эту интересную статью о потоке или эту статью о потоке . После повторной сборки потока вы можете удалить внешний текст (например, Кому, От, CC и т. Д.), И все готово.

Если сообщения, с которыми вы работаете, не имеют заголовков, вы также можете использовать сопоставление сходства, чтобы определить, какие части письма являются текстом ответа. В этом случае вы застряли с сопоставлением сходства, чтобы определить повторяющийся текст. В этом случае вы, возможно, захотите взглянуть на алгоритм Левенштейновского расстояния , такой как этот в Code Project или этот .

Независимо от того, что, если вы заинтересованы в процессе создания потоков, посмотрите этот замечательный PDF-файл о повторной сборке потоков электронной почты .

Когда у вас нет темы:

Если вы застряли только в одном сообщении из цепочки, вам нужно попытаться угадать, что это за цитата. В этом случае, вот различные методы цитаты, которые я видел:

  1. линия (как видно в перспективе).
  2. Угловые скобки
  3. "--- Исходное сообщение ---"
  4. "В такой-то день такой-то писал:"

Удалите текст оттуда, и все готово. Недостатком любого из них является то, что все они предполагают, что отправитель поместил свой ответ поверх цитируемого текста и не чередовал его (как это было в старом стиле в Интернете). Если это произойдет, удачи. Я надеюсь, что это поможет некоторым из вас там

28 голосов
/ 23 января 2009

Прежде всего, это сложная задача.

Вы должны собрать типичные ответы от разных почтовых клиентов и подготовить правильные регулярные выражения (или что-то еще) для их анализа. Я собрал ответы от 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

С уважением, Олег Ярошевич

23 голосов
/ 11 сентября 2011

Спасибо, Голег, за регулярные выражения! Действительно помог. Это не 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

Пока все работает довольно хорошо.

11 голосов
/ 13 июля 2012

Самый простой способ сделать это - разместить маркер в вашем контенте, например:

--- Пожалуйста, ответьте над этой строкой ---

Как вы, несомненно, заметили, разбор цитируемого текста не является тривиальной задачей, поскольку разные почтовые клиенты цитируют текст по-разному. Для правильного решения этой проблемы вам необходимо учесть и протестировать в каждом почтовом клиенте.

Facebook может сделать это, но если у вашего проекта большой бюджет, вы, вероятно, не сможете.

Олег решил проблему с помощью регулярных выражений, чтобы найти текст «13 июля 2012 г., 13:09, xxx wrote:». Однако если пользователь удалит этот текст или ответит внизу письма, как это делают многие люди, это решение не будет работать.

Аналогично, если почтовый клиент использует другую строку даты или не содержит строку даты, регулярное выражение не будет выполнено.

6 голосов
/ 16 февраля 2013

Вот моя C # версия кода Ruby @ hurshagrawal. Я не очень хорошо знаю Руби, так что это может быть не так, но я думаю, что понял все правильно.

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 голосов
/ 10 ноября 2008

В электронном письме нет универсального индикатора ответа. Лучшее, что вы можете сделать, это попытаться поймать наиболее распространенные и проанализировать новые шаблоны, когда вы сталкиваетесь с ними.

Имейте в виду, что некоторые люди вставляют ответы в цитируемый текст (например, мой начальник отвечает на вопросы в той же строке, что и я их), поэтому, что бы вы ни делали, вы можете потерять некоторую информацию, которую хотели бы сохранить.

3 голосов
/ 02 июня 2011

Если вы управляете исходным сообщением (например, уведомлениями из веб-приложения), вы можете поместить отдельный идентифицируемый заголовок на место и использовать его в качестве разделителя для исходного сообщения.

1 голос
/ 02 апреля 2013

Это хорошее решение. Нашел после долгих поисков.

Одно добавление, как упомянуто выше, относится к случаю, поэтому вышеприведенные выражения неправильно проанализировали мои ответы 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),

Приветствия

0 голосов
/ 30 мая 2018

Если вы используете SigParser.com API, он предоставит вам массив всех разбитых писем в цепочке ответов из одной текстовой строки. Так что, если есть 10 писем, вы получите текст для всех 10 писем.

enter image description here

Подробную спецификацию API можно посмотреть здесь.

https://api.sigparser.com/

enter image description here

0 голосов
/ 20 октября 2016

Это старый пост, однако, вы не уверены, знаете ли вы, что github имеет Ruby lib , извлекающий ответ. Если вы используете .NET, у меня есть .NET один на https://github.com/EricJWHuang/EmailReplyParser

...