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


87

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


2
Тебе с этим повезло? Я хочу сделать то же самое.
steve_c

какое-либо окончательное решение с полным образцом исходного кода, работающим над этим?
Kiquenet

Quotequail делает это на Python
philfreo

Может ли кто-нибудь помочь с его версией php?
user4271704

Ответы:


60

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

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

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

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

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

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

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

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

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


32

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

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

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


Что делать, если я не знаю адрес электронной почты?
harsimranb

@ Shyamal-Parikh, это не сработает для html-писем, но обычно текстовое сообщение также включается в электронные сообщения
maembe

25

Спасибо, Голег, за регулярные выражения! Действительно помогло. Это не C #, но для гуглеров вот мой сценарий синтаксического анализа 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
Вам следует создать вопрос Ruby и ответить на него этим кодом, а не размещать его на вопросе c #.
Matthieu

6
@Matthieu, это не просто вопрос C #, это вопрос электронной почты и ее анализа. на мой взгляд абсолютно актуально.
Трент,

@Trent: тогда следует удалить тег C #.
Matthieu

7
Самое забавное, что я нашел этот вопрос в Google по теме (а не по языку), и мне действительно нужно было что-то реализовать в Ruby. Итак, ура!
bratsche

2
Это лучший ответ на данный момент. Regex довольно не зависит от языка. Спасибо за публикацию
superluminary

11

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

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

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

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

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

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


Этот подход не дает ответов на ответы, если вы не помещаете эту строку каждый раз, когда отвечаете.
jpw

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

Это должен быть принятый ответ. Однако я бы добавил информацию, что ответ не удастся, если строка будет удалена.
Бенни

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

@superluminary Я имел ввиду, я бы добавил его в строчку. Так это что-то вроде -- Please reply above this line. DO NOT REMOVE IT! --. Кроме того, я испытал, что это не всегда работает, поскольку некоторые почтовые клиенты добавляют xxx wrote on <datetime>:строку перед всей цитатой и, следовательно, перед этой строкой. Эта строка может быть проанализирована с помощью регулярного выражения, однако она может быть на разных языках и в другом формате, поскольку почтовые клиенты отличаются.
Benni

6

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

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


Gmail делает это ... по крайней мере, кажется, что это делает. Насколько я помню, есть идентификатор темы, который не меняется между оригиналом и ответами ...
Кенни

gmail может добавить '>', как и другие почтовые клиенты, но это не стандарт электронной почты и не то, на что вы можете рассчитывать
3Doubloons

5

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

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

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


0

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

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

Ура


Может ли кто-нибудь помочь с его версией php?
user4271704


-1

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


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

вы постоянно обновляете эту библиотеку? Я пришел на поиски, потому что библиотека C # не разбирает должным образом простое электронное письмо из Outlook из Office 365. Затем я просмотрел исходный код Ruby и обнаружил, что в их тестовых примерах есть идентичный тестовый пример, поэтому они явно думают, что должны анализировать Это.
Грег Верес

-1

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

введите описание изображения здесь

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

https://api.sigparser.com/

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.