Estou tentando descobrir como analisar o texto de um e-mail a partir de qualquer texto de resposta citado que ele possa incluir. Percebi que normalmente os clientes de e-mail colocam "Em tal e tal data fulano escreveu" ou prefixam as linhas com um colchete angular. Infelizmente, nem todo mundo faz isso. Alguém tem alguma ideia de como detectar programaticamente o texto de resposta? Estou usando C # para escrever este analisador.
c#
ruby
email
email-parsing
VanOrman
fonte
fonte
Respostas:
Pesquisei muito mais sobre isso e aqui está o que encontrei. Existem basicamente duas situações em que você está fazendo isso: quando você tem o tópico inteiro e quando não. Vou dividi-lo nessas duas categorias:
Quando você tiver o tópico:
Se você tiver toda a série de e-mails, poderá obter um nível muito alto de garantia de que o que está removendo é realmente um texto citado. Existem duas maneiras de fazer isso. Um, você poderia usar o Message-ID, o In-Reply-To ID e o Thread-Index da mensagem para determinar a mensagem individual, seu pai e o thread a que pertence. Para obter mais informações sobre isso, consulte RFC822 , RFC2822 , este artigo interessante sobre segmentação ou este artigo sobre segmentação . Depois de ter remontado o tópico, você pode remover o texto externo (como as linhas Para, De, CC, etc ...) e pronto.
Se as mensagens com as quais você está trabalhando não tiverem cabeçalhos, você também pode usar a correspondência de similaridade para determinar quais partes de um e-mail são o texto de resposta. Nesse caso, você está preso em fazer correspondência por similaridade para determinar o texto que se repete. Neste caso, você pode querer examinar um algoritmo de distância de Levenshtein como este no Projeto de código ou este .
Não importa o que aconteça, se você estiver interessado no processo de threading, verifique este ótimo PDF sobre remontagem de threads de email .
Quando você não tem o tópico:
Se você está preso a apenas uma mensagem do tópico, terá que tentar adivinhar qual é a citação. Nesse caso, aqui estão os diferentes métodos de cotação que vi:
Remova o texto de lá e pronto. A desvantagem de qualquer um deles é que todos presumem que o remetente colocou sua resposta sobre o texto citado e não o intercalou (como era o estilo antigo na Internet). Se isso acontecer, boa sorte. Espero que isso ajude alguns de vocês!
fonte
Em primeiro lugar, essa é uma tarefa complicada.
Você deve coletar respostas típicas de diferentes clientes de e-mail e preparar expressões regulares corretas (ou qualquer outra coisa) para analisá-las. Coletei respostas do outlook, thunderbird, gmail, apple mail e mail.ru.
Estou usando expressões regulares para analisar a resposta da seguinte maneira: se a expressão não corresponder, tento usar a próxima.
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);
Para remover a cotação no final:
new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline);
Aqui está minha pequena coleção de respostas de teste (amostras divididas por --- ):
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
Atenciosamente, Oleg Yaroshevych
fonte
Obrigado, Goleg, pelas regexes! Realmente ajudou. Este não é C #, mas para os googlers por aí, aqui está meu script de análise 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
Funcionou muito bem até agora.
fonte
De longe, a maneira mais fácil de fazer isso é colocando um marcador em seu conteúdo, como:
--- Por favor, responda acima desta linha ---
Como você sem dúvida notou, analisar o texto citado não é uma tarefa trivial, pois diferentes clientes de email citam o texto de maneiras diferentes. Para resolver este problema adequadamente, você precisa contabilizar e testar cada cliente de e-mail.
O Facebook pode fazer isso, mas a menos que seu projeto tenha um grande orçamento, você provavelmente não pode.
Oleg resolveu o problema usando regexes para encontrar o texto "Em 13 de julho de 2012, às 13h09, xxx escreveu:" No entanto, se o usuário excluir esse texto ou responder na parte inferior do e-mail, como muitas pessoas fazem, essa solução não funcionará.
Da mesma forma, se o cliente de e-mail usar uma string de data diferente ou não incluir uma string de data, o regex falhará.
fonte
-- Please reply above this line. DO NOT REMOVE IT! --
. Além disso, o que percebi é que nem sempre funciona, pois alguns clientes de e-mail adicionam umaxxx wrote on <datetime>:
linha antes de toda a cotação e, portanto, antes dessa linha. Essa linha pode ser analisada com regex, no entanto, pode estar em diferentes idiomas e em um formato diferente, pois os clientes de e-mail são diferentes.Não existe um indicador universal de resposta em um e-mail. O melhor que você pode fazer é tentar capturar os padrões mais comuns e analisar novos padrões conforme você os encontra.
Lembre-se de que algumas pessoas inserem respostas dentro do texto citado (meu chefe, por exemplo, responde às perguntas na mesma linha que eu as perguntei), portanto, faça o que fizer, poderá perder algumas informações que gostaria de manter.
fonte
Aqui está minha versão C # do código Ruby de @hurshagrawal. Não conheço Ruby muito bem, então poderia estar errado, mas acho que entendi direito.
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(); }
fonte
Se você controlar a mensagem original (por exemplo, notificações de um aplicativo da web), poderá colocar um cabeçalho distinto e identificável no lugar e usá-lo como delimitador para a postagem original.
fonte
Esta é uma boa solução. Encontrei depois de procurar por tanto tempo.
Uma adição, conforme mencionado acima, é caso a caso, portanto, as expressões acima não analisaram corretamente minhas respostas do gmail e do Outlook (2010), para as quais adicionei os dois Regex a seguir. Deixe-me saber se houver qualquer problema.
//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),
Felicidades
fonte
É um post antigo, entretanto, não tenho certeza se você sabe que o github tem uma lib Ruby extraindo a resposta. Se você usa .NET, eu tenho um .NET em https://github.com/EricJWHuang/EmailReplyParser
fonte
Se você usar a API do SigParser.com , ela fornecerá uma série de todos os e-mails quebrados em uma cadeia de resposta a partir de uma única string de texto de e-mail. Portanto, se houver 10 e-mails, você receberá o texto de todos os 10 e-mails.
Você pode ver as especificações detalhadas da API aqui.
https://api.sigparser.com/
fonte