Разбирайте содержимое электронной почты из указанного ответа

Я пытаюсь выяснить, как разобрать текст письма из любого цитируемого текста ответа, который он может включить. Я заметил, что обычно почтовые клиенты будут помещать "В такую ​​и такую ​​дату так и так написано" или префикс строк с помощью угловой скобки. К сожалению, не все это делают. Кто-нибудь имеет представление о том, как программно обнаруживать текст ответа? Я использую С# для написания этого синтаксического анализатора.

Ответ 1

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

Когда у вас есть поток:

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

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

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

Если у вас нет потока:

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

  • строка (как видно в Outlook).
  • Угловые скобки
  • "--- Оригинальное сообщение ---"
  • "В такой-то день, так и так писал:"

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

Ответ 2

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

Вы должны собирать типичные ответы от разных почтовых клиентов и готовить правильные регулярные выражения (или что-то еще) для их анализа. Я собрал ответы от 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: [email protected] [mailto:[email protected]] 
Sent: Tuesday, January 13, 2009 1:27 PM
----
2008/12/26 <[email protected]>

>  text
----
[email protected] wrote:
> text
----
      [email protected] wrote:         text
text
----
2009/1/13 <[email protected]>

>  text
----
 [email protected] wrote:         text
 text
----
2009/1/13 <[email protected]>

> text
> text
----
2009/1/13 <[email protected]>

> text
> text
----
[email protected] wrote:
> text
> text
<response here>
----
--- On Fri, 23/1/09, [email protected] <[email protected]> wrote:

> text
> text

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

Ответ 3

Спасибо, Goleg, для регулярных выражений! Действительно помог. Это не С#, но для googlers там, здесь мой анализ Ruby script:

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

До сих пор он работал очень хорошо.

Ответ 4

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

--- Пожалуйста, ответьте выше этой строки ---

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

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

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

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

Ответ 5

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

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

Ответ 6

Вот моя версия С# для кода Ruby @hurshagrawal. Я не очень хорошо знаю 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();
}

Ответ 7

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

Ответ 8

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

Одно из добавлений, как упоминалось выше, это случайное, поэтому приведенные выше выражения не правильно анализировали мои ответы gmail и outlook (2010), для которых я добавил следующие два Regex (s). Сообщите мне о любых проблемах.

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

Приветствия

Ответ 9

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