Я пытаюсь понять, как разобрать текст электронного письма из любого цитируемого текста ответа, который он может включать. Я заметил, что обычно почтовые клиенты ставят «В такую-то дату такой-то и такой-то писал» или перед строками ставят угловую скобку. К сожалению, не все так поступают. Кто-нибудь знает, как программно определить текст ответа? Я использую C # для написания этого парсера.
c#
ruby
email
email-parsing
VanOrman
источник
источник
Ответы:
Я много искал по этому поводу, и вот что я нашел. В основном есть две ситуации, в которых вы делаете это: когда у вас есть весь поток, и когда у вас его нет. Я разделю его на две категории:
Когда у вас есть поток:
Если у вас есть вся серия электронных писем, вы можете быть уверены в том, что удаляемый вами текст на самом деле является цитируемым. Есть два способа сделать это. Во-первых, вы можете использовать Message-ID, In-Reply-To ID и Thread-Index для определения отдельного сообщения, его родителя и потока, которому оно принадлежит. Для получения дополнительной информации см. RFC822 , RFC2822 , эту интересную статью о многопоточности или эту статью о многопоточности . После того, как вы повторно собрали поток, вы можете удалить внешний текст (например, строки To, From, CC и т. Д.), И все готово.
Если сообщения, с которыми вы работаете, не имеют заголовков, вы также можете использовать сопоставление схожести, чтобы определить, какие части электронного письма являются текстом ответа. В этом случае вы застряли в сопоставлении схожести, чтобы определить повторяющийся текст. В этом случае вы можете захотеть изучить алгоритм расстояния Левенштейна, такой как этот в Code Project или этот .
Несмотря ни на что, если вас интересует процесс создания цепочек писем , посмотрите этот отличный PDF-файл о повторной сборке цепочек писем .
Когда у вас нет потока:
Если вы застряли только на одном сообщении из цепочки, вам нужно попытаться угадать, что это за цитата. В этом случае я видел различные методы цитирования:
Удалите текст оттуда вниз, и все готово. Обратной стороной любого из них является то, что все они предполагают, что отправитель поместил свой ответ поверх цитируемого текста, а не чередовал его (как это было в старом стиле в Интернете). Если это произойдет, удачи. Надеюсь, это поможет некоторым из вас!
источник
Прежде всего, это непростая задача.
Вам следует собрать типичные ответы от разных почтовых клиентов и подготовить правильные регулярные выражения (или что-то еще) для их анализа. Я собрал ответы из 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
С уважением, Олег Ярошевич
источник
Спасибо, Голег, за регулярные выражения! Действительно помогло. Это не 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
Пока это работает очень хорошо.
источник
Безусловно, самый простой способ сделать это - разместить маркер в вашем контенте, например:
--- Пожалуйста, ответьте над этой строкой ---
Как вы, несомненно, заметили, разбор цитируемого текста - нетривиальная задача, поскольку разные почтовые клиенты цитируют текст по-разному. Чтобы правильно решить эту проблему, вам нужно учитывать и тестировать в каждом почтовом клиенте.
Facebook может это сделать, но, если у вашего проекта нет большого бюджета, вы, вероятно, не сможете.
Олег решил проблему с помощью регулярных выражений, чтобы найти текст «13 июля 2012 г., 13:09, xxx написал:». Однако, если пользователь удалит этот текст или ответит внизу письма, как это делают многие люди, это решение не сработает.
Аналогично, если почтовый клиент использует другую строку даты или не включает строку даты, регулярное выражение завершится ошибкой.
источник
-- Please reply above this line. DO NOT REMOVE IT! --
. Кроме того, я испытал, что это не всегда работает, поскольку некоторые почтовые клиенты добавляютxxx wrote on <datetime>:
строку перед всей цитатой и, следовательно, перед этой строкой. Эта строка может быть проанализирована с помощью регулярного выражения, однако она может быть на разных языках и в другом формате, поскольку почтовые клиенты отличаются.Универсального индикатора ответа в электронном письме нет. Лучшее, что вы можете сделать, - это попытаться поймать наиболее распространенные и проанализировать новые шаблоны по мере их появления.
Имейте в виду, что некоторые люди вставляют ответы в цитируемый текст (например, мой начальник отвечает на вопросы в той же строке, что и я), поэтому, что бы вы ни делали, вы можете потерять некоторую информацию, которую хотели бы сохранить.
источник
Вот моя версия кода 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(); }
источник
Если вы управляете исходным сообщением (например, уведомлениями из веб-приложения), вы можете поместить отдельный идентифицируемый заголовок и использовать его в качестве разделителя для исходного сообщения.
источник
Это хорошее решение. Нашел после стольких поисков.
Одно из дополнений, как упоминалось выше, касается случая, поэтому приведенные выше выражения некорректно анализировали мои ответы 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, у меня есть .NET по адресу https://github.com/EricJWHuang/EmailReplyParser.
источник
Если вы используете API SigParser.com , он предоставит вам массив всех разбитых писем в цепочке ответов из одной текстовой строки электронного письма. Итак, если есть 10 писем, вы получите текст для всех 10 писем.
Вы можете просмотреть подробную спецификацию API здесь.
https://api.sigparser.com/
источник