Проаналізуйте вміст електронної пошти з цитованої відповіді


86

Я намагаюся зрозуміти, як проаналізувати текст електронного листа з будь-якого цитованого тексту відповіді, який він може містити. Я помітив, що зазвичай поштові клієнти ставлять "На таку-то дату так і так писали" або ставлять префікси до рядків кутовою дужкою. На жаль, не всі цим займаються. Хтось має ідею про те, як програмно виявити текст відповіді? Я використовую C # для написання цього парсера.


2
Вам пощастило з цим? Я хочу зробити те саме.
steve_c

будь-яке остаточне рішення із повним зразком вихідного коду, що працює над цим?
Kiquenet

Quotequail робить це в Python
philfreo

Хто-небудь може допомогти для його версії PHP?
user4271704

Відповіді:


60

Я набагато більше займався цим, і ось що я знайшов. В основному є дві ситуації, коли ви робите це: коли у вас є ціла нитка, а коли у вас немає. Я поділю це на ці дві категорії:

Коли у вас є нитка:

Якщо у вас є ціла серія електронних листів, ви можете досягти дуже високого рівня впевненості, що те, що ви видаляєте, насправді є цитованим текстом. Це можна зробити двома способами. По-перше, ви можете використовувати ідентифікатор повідомлення, ідентифікатор відповіді та індекс потоку повідомлення, щоб визначити окреме повідомлення, його батьківське повідомлення та потік, якому воно належить. Для отримання додаткової інформації про це дивіться RFC822 , RFC2822 , цю цікаву статтю про різьбу або цю статтю про різьбу . Після того, як ви заново зібрали потік, ви можете видалити зовнішній текст (наприклад, До, Від, CC тощо) і все готово.

Якщо повідомлення, з якими ви працюєте, не мають заголовків, ви також можете скористатися відповідністю подібності, щоб визначити, які частини електронного листа є текстом відповіді. У цьому випадку ви застрягли в порівнянні подібностей, щоб визначити текст, який повторюється. У цьому випадку вам може знадобитися вивчити алгоритм відстані Левенштейна, такий як цей на Code Project або цей .

Незважаючи ні на що, якщо ви зацікавлені в процесі потокової роботи, перегляньте цей чудовий PDF-файл про повторне збирання ланцюжків електронної пошти .

Коли у вас немає теми:

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

  1. лінія (як видно з перспективи).
  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
Вам слід зробити рубінове запитання і відповісти на нього цим кодом, замість того, щоб публікувати його на запитанні ac #.
Matthieu

6
@Matthieu, це не просто запитання на C #, а питання електронної пошти та розбору електронної пошти. на мій погляд абсолютно доречно.
Трент

@Trent: тоді тег C # слід скинути.
Матьє

7
Найцікавіше, що я знайшов це питання Гуглінгом щодо теми (а не мови), і мені насправді потрібно було щось реалізувати в Ruby. Отже, ура!
bratsche

2
Це найкраща відповідь на сьогодні. Regex - досить сильний агностик мови. Дякуємо за публікацію
суперсвітлий

11

На сьогоднішній день найпростіший спосіб це зробити, розмістивши маркер у вашому вмісті, наприклад:

--- Будь ласка, дайте відповідь над цим рядком ---

Як ви, без сумніву, помітили, розбір цитованого тексту не є тривіальним завданням, оскільки різні поштові клієнти цитують текст різними способами. Щоб правильно вирішити цю проблему, вам потрібно врахувати та протестувати кожен поштовий клієнт.

Facebook може це зробити, але якщо ваш проект не має великого бюджету, ви, мабуть, не зможете.

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

Подібним чином, якщо поштовий клієнт використовує інший рядок дати або не включає рядок дати, регулярний вираз зазнає помилки.


Цей підхід не дає результатів у відповідях на відповіді, якщо ви не ставите цей рядок кожного разу, коли відповідаєте.
jpw

1
Так, він має недоліки. Якщо користувач видаляє відповідь над рядком рядка, тоді ваша відповідь не вдасться. Я розумію цей випадок і надсилаю користувачеві пряме повідомлення, повідомляючи його про помилку, із посиланням на відповідь через веб-програму. Здається, більшість користувачів можуть використовувати його без зайвих проблем.
надсвітлий

Це має бути прийнятою відповіддю. Однак я б додав інформацію про те, що відповідь не буде успішною, якщо рядок буде видалено.
Бенні

@Benni - так, це не вдасться, якщо рядок буде видалено. На жаль, не існує єдиного стандартного способу цитування тексту в поштових клієнтах. Якщо рядок видалено, ви можете розглядати весь текст як відповідь. Я не думаю, що в цьому випадку ідеальне рішення неможливе.
надсвітлий

@superluminary, я мав на увазі, я б додав його до рядка. Отже, це щось на зразок -- Please reply above this line. DO NOT REMOVE IT! --. Крім того, я переконався, що це не завжди буде працювати, оскільки деякі поштові клієнти додають xxx wrote on <datetime>:рядок перед цілою ціною, а отже і перед цим рядком. Цей рядок може бути проаналізований регулярним виразом, однак він може бути різними мовами та в іншому форматі, оскільки поштові клієнти відрізняються.
Бенні

6

У електронному листі не існує універсального показника відповіді. Найкраще, що ви можете зробити, - це намагатися вловлювати найпоширеніші та аналізувати нові шаблони, коли ви натрапляєте на них.

Майте на увазі, що деякі люди вставляють відповіді всередині цитованого тексту (наприклад, мій бос відповідає на запитання в тому ж рядку, що і я їх задавав), тому, що б ви не робили, ви можете втратити деяку інформацію, яку хотіли б зберегти.


gmail робить це ... принаймні, здається, це робить. Наскільки я пам’ятаю, є ідентифікатор нитки, який не змінюється між оригіналом та відповідями ...
kenny

gmail може додати '>', як це роблять інші поштові клієнти, але це не стандарт електронних листів і не те, на що можна розраховувати
3Doubloons

5

Ось моя 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();
}

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 lib, який витягує відповідь. Якщо ви використовуєте .NET, у мене є .NET на https://github.com/EricJWHuang/EmailReplyParser


1
Посилання на зовнішні ресурси заохочуються, але, будь ласка, додайте контекст навколо посилання, щоб ваші однодумці мали певне уявлення, що це таке і чому воно існує. Завжди цитуйте найбільш релевантну частину важливого посилання, якщо цільовий сайт недоступний або постійно перебуває в автономному режимі.
pableiros

Ви постійно оновлюєте цю бібліотеку? Я прийшов шукати, тому що бібліотека C # неправильно розбирає просту електронну пошту з Outlook з Office 365. Потім я заглянув у рубіновий вихідний код і виявив, що в їхніх тестах був однаковий тестовий приклад, так чітко вони думають, що їм слід проаналізувати це.
Грег Верес

-1

Якщо ви використовуєте API SigParser.com , він надасть вам масив усіх розбитих листів у ланцюжку відповідей з одного текстового рядка електронної пошти. Отже, якщо є 10 електронних листів, ви отримаєте текст для всіх 10 електронних листів.

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

Ви можете переглянути докладну специфікацію API тут.

https://api.sigparser.com/

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.