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.
c#
ruby
email
email-parsing
VanOrman
źródło
źródło
Odpowiedzi:
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:
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!
źródło
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: [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
Pozdrawiam Oleg Jaroszewycz
źródło
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.
źródło
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.
źródło
-- 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ę.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ć.
źródło
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(); }
źródło
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.
źródło
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
źródło
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
źródło
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.
Możesz zobaczyć szczegółową specyfikację API tutaj.
https://api.sigparser.com/
źródło