Przeanalizuj treść wiadomości e-mail z cytowanej odpowiedzi


87

Próbuję wymyślić, jak wyodrębnić treść wiadomości e-mail z dowolnego cytowanego tekstu odpowiedzi, który może zawierać. Zauważyłem, że zwykle klienci poczty e-mail umieszczają informację „W taką a taką datę, tak a tak napisałem” lub poprzedzają wiersze nawiasami ostrymi. Niestety nie wszyscy to robią. Czy ktoś ma pomysł, jak programowo wykrywać tekst odpowiedzi? Używam C # do napisania tego parsera.


2
Czy miałeś z tym szczęście? Chcę zrobić dokładnie to samo.
— steve_c

jakieś ostateczne rozwiązanie z pełną próbką kodu źródłowego nad tym działającą?
— Kiquenet

Quotequail robi to w Pythonie
— philfreo

Czy ktoś może pomóc w jego wersji php?
— user4271704

Odpowiedzi:


60

Poszukałem o wiele więcej i oto, co znalazłem. Istnieją zasadniczo dwie sytuacje, w których to robisz: kiedy masz cały wątek i kiedy nie. Podzielę to na te dwie kategorie:

Kiedy masz wątek:

Jeśli masz całą serię e-maili, możesz uzyskać bardzo wysoki poziom pewności, że usuwany tekst to w rzeczywistości cytowany tekst. Można to zrobić na dwa sposoby. Po pierwsze, możesz użyć identyfikatora wiadomości, identyfikatora odpowiedzi do wiadomości i indeksu wątku, aby określić pojedynczą wiadomość, jej element nadrzędny i wątek, do którego należy. Aby uzyskać więcej informacji na ten temat, zobacz RFC822 , RFC2822 , ten interesujący artykuł o wątkach lub ten artykuł o wątkach . Po ponownym złożeniu wątku możesz usunąć tekst zewnętrzny (taki jak wiersze Do, Od, DW itp.) I gotowe.

Jeśli wiadomości, z którymi pracujesz, nie mają nagłówków, możesz również użyć dopasowania podobieństwa, aby określić, które części wiadomości e-mail stanowią tekst odpowiedzi. W takim przypadku utkniesz z dopasowywaniem podobieństwa w celu określenia powtarzanego tekstu. W takim przypadku możesz zajrzeć do algorytmu odległości Levenshteina, takiego jak ten w Code Project lub ten .

Bez względu na wszystko, jeśli interesuje Cię proces tworzenia wątków, zapoznaj się z tym wspaniałym plikiem PDF na temat ponownego składania wątków e-mail .

Gdy nie masz wątku:

Jeśli utkniesz z tylko jedną wiadomością z wątku, musisz spróbować odgadnąć, jaki jest cytat. W takim przypadku oto różne metody cytowania, które widziałem:

  1. linia (jak widać w programie Outlook).
  2. Wsporniki kątowe
  3. "---Wiadomość oryginalna---"
  4. „W taki a taki dzień taki a taki dzień napisał:”

Usuń stamtąd tekst i gotowe. Wadą każdego z nich jest to, że wszyscy zakładają, że nadawca umieścił swoją odpowiedź na cytowanym tekście i nie przeplatał jej (jak to było w starym stylu w Internecie). Jeśli tak się stanie, powodzenia. Mam nadzieję, że to pomoże niektórym z was!


32

Przede wszystkim jest to trudne zadanie.

Powinieneś zebrać typowe odpowiedzi od różnych klientów poczty e-mail i przygotować prawidłowe wyrażenia regularne (lub cokolwiek innego), aby je przeanalizować. Zebrałem odpowiedzi z programów Outlook, thunderbird, gmail, apple mail i mail.ru.

Używam wyrażeń regularnych do analizowania odpowiedzi w następujący sposób: jeśli wyrażenie nie zostało dopasowane, próbuję użyć następnego.

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);

Aby na końcu usunąć cytat:

new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline);

Oto moja mała kolekcja odpowiedzi testowych (próbki podzielone przez --- ):

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

Pozdrawiam Oleg Jaroszewycz


A jeśli nie znam adresu e-mail?
— harsimranb

@ Shyamal-Parikh to nie zadziała w przypadku wiadomości e-mail w formacie HTML, ale zazwyczaj do wiadomości e-mail dołączana jest również wiadomość w postaci zwykłego tekstu
— maembe

25

Dziękuję Goleg za wyrażenia regularne! Naprawdę pomogło. To nie jest C #, ale dla pracowników Google, oto mój skrypt analizujący Rubiego:

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

Jak dotąd działało całkiem nieźle.


1
Powinieneś zadać pytanie rubinowe i odpowiedzieć na nie tym kodem, zamiast umieszczać je na pytaniu ac #.
— Matthieu

6
@Matthieu, to nie tylko pytanie C #, ale pytanie dotyczące analizy wiadomości e-mail i wiadomości e-mail. moim zdaniem całkowicie istotne.
— Trent

@Trent: tag C # powinien zostać usunięty.
— Matthieu,

7
Zabawne jest to, że znalazłem to pytanie, szukając w Google tematu (nie języka) i właściwie musiałem zaimplementować coś w Rubim. Więc, zdrowie!
— bratsche

2
To najlepsza jak dotąd odpowiedź. Regex jest całkiem niezależny od języka. Dzięki za wysłanie wiadomości
— superluminary

11

Zdecydowanie najłatwiej to zrobić, umieszczając w treści znacznik, na przykład:

--- Odpowiedz powyżej tej linii ---

Jak zapewne zauważyłeś, parsowanie cytowanego tekstu nie jest łatwym zadaniem, ponieważ różni klienci poczty e-mail cytują tekst na różne sposoby. Aby poprawnie rozwiązać ten problem, musisz uwzględnić i przetestować w każdym kliencie poczty e-mail.

Facebook może to zrobić, ale jeśli Twój projekt nie ma dużego budżetu, prawdopodobnie nie możesz.

Oleg rozwiązał ten problem, używając wyrażeń regularnych, aby znaleźć tekst „13 lipca 2012 r. O godzinie 13:09 xxx napisał:”. Jeśli jednak użytkownik usunie ten tekst lub odpowie na dole wiadomości e-mail, jak robi to wiele osób, to rozwiązanie nie zadziała.

Podobnie, jeśli klient poczty e-mail używa innego ciągu daty lub nie zawiera ciągu daty, wyrażenie regularne zakończy się niepowodzeniem.


To podejście zawodzi w przypadku odpowiedzi na odpowiedzi, chyba że umieścisz tę linię za każdym razem, gdy odpowiadasz.
— jpw

1
Tak, ma wady. Jeśli użytkownik usunie odpowiedź powyżej ciągu linii, twoja odpowiedź nie powiedzie się. Łapię tę sprawę i wysyłam użytkownikowi bezpośrednią wiadomość z linkiem do odpowiedzi za pośrednictwem aplikacji internetowej, informując go, że wiadomość się nie powiodła. Większość użytkowników wydaje się być w stanie z niego korzystać bez większych problemów.
— superluminary

To powinna być akceptowana odpowiedź. Dodałbym jednak informację, że odpowiedź nie powiedzie się, jeśli wiersz zostanie usunięty.
— Benni

@Benni - tak, nie powiedzie się, jeśli linia zostanie usunięta. Niestety nie ma jednego standardowego sposobu cytowania tekstu w programach pocztowych. W przypadku, gdy wiersz zostanie usunięty, możesz traktować cały tekst jako odpowiedź. Nie sądzę, aby w tym przypadku możliwe było idealne rozwiązanie.
— superluminium,

@superluminary Chodziło mi o to, że dodam go do wiersza. Więc to jest coś takiego -- Please reply above this line. DO NOT REMOVE IT! --. Doświadczyłem również tego, że nie zawsze będzie to działać, ponieważ niektórzy klienci poczty e-mail dodają xxx wrote on <datetime>:linię przed całym cytatem, a zatem przed tą linią. Ten wiersz można przeanalizować za pomocą wyrażenia regularnego, ale może być w różnych językach i w innym formacie, ponieważ klienci poczty e-mail różnią się.
— Benni,

6

Nie ma uniwersalnego wskaźnika odpowiedzi w e-mailu. Najlepsze, co możesz zrobić, to spróbować wyłapać najpopularniejsze i przeanalizować nowe wzorce, gdy je napotkasz.

Pamiętaj, że niektórzy ludzie wstawiają odpowiedzi w zacytowanym tekście (na przykład mój szef odpowiada na pytania w tym samym wierszu, co ich zadałem), więc cokolwiek zrobisz, możesz stracić część informacji, które chciałbyś zachować.


gmail to robi ... przynajmniej tak się wydaje. Z tego, co pamiętam, jest identyfikator wątku, który nie zmienia się między oryginałem a odpowiedziami ...
— kenny

gmail może dodać znaki „>”, podobnie jak inni klienci poczty e-mail, ale nie jest to standard wiadomości e-mail i nie jest to coś, na co można liczyć
— 3Doubloons

5

Oto moja wersja C # kodu Ruby @ hurshagrawal. Nie znam dobrze Ruby, więc może być wyłączone, ale myślę, że dobrze zrozumiałem.

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

Jeśli kontrolujesz oryginalną wiadomość (np. Powiadomienia z aplikacji internetowej), możesz umieścić odrębny, możliwy do zidentyfikowania nagłówek i użyć go jako separatora dla oryginalnego postu.


0

To jest dobre rozwiązanie. Znalazłem go po tak długich poszukiwaniach.

Jeden dodatek, jak wspomniano powyżej, ma znaczenie dla wielkości liter, więc powyższe wyrażenia nie przeanalizowały poprawnie moich odpowiedzi na Gmaila i Outlooka (2010), dla których dodałem następujące dwa Regex (y). Daj mi znać w razie jakichkolwiek problemów.

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

Twoje zdrowie


Czy ktoś może pomóc w jego wersji php?
— user4271704


-1

Jest to stary post, jednak nie jesteś pewien, czy wiesz, że github ma bibliotekę Ruby, która wyodrębnia odpowiedź. Jeśli używasz .NET, mam .NET na https://github.com/EricJWHuang/EmailReplyParser


1
Zachęcamy do umieszczania linków do zewnętrznych zasobów, ale proszę dodać kontekst wokół łącza, aby inni użytkownicy mieli pojęcie, co to jest i dlaczego się tam znajduje. Zawsze cytuj najbardziej odpowiednią część ważnego linku, na wypadek gdyby strona docelowa była nieosiągalna lub została trwale wyłączona.
— pableiros

czy aktualizujesz tę bibliotekę? Przyszedłem do wyszukiwania, ponieważ biblioteka C # nie analizuje poprawnie prostej wiadomości e-mail z Outlooka z Office 365. Następnie zajrzałem do kodu źródłowego Ruby i stwierdziłem, że w ich przypadkach testowych był identyczny przypadek testowy, więc wyraźnie uważają, że powinni przeanalizować to.
— Greg Veres

-1

Jeśli korzystasz z API SigParser.com , otrzymasz tablicę wszystkich e-maili podzielonych na łańcuch odpowiedzi z jednego ciągu tekstowego wiadomości e-mail. Jeśli więc jest 10 e-maili, otrzymasz tekst wszystkich 10 e-maili.

wprowadź opis obrazu tutaj

Możesz zobaczyć szczegółową specyfikację API tutaj.

https://api.sigparser.com/

wprowadź opis obrazu tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.