Analisar o conteúdo do e-mail a partir da resposta citada


87

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.


2
Você teve alguma sorte com isso? Estou procurando fazer exatamente a mesma coisa.
steve_c

alguma solução final com amostra de código-fonte completa trabalhando nisso?
Kiquenet 18/06/2013

Quotequail faz isso em Python
philfreo

Alguém pode ajudar por sua versão php?
user4271704

Respostas:


60

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:

  1. uma linha (como visto no outlook).
  2. Suportes angulares
  3. "---Mensagem original---"
  4. "Em tal e tal dia, fulano escreveu:"

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!


32

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: 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

Atenciosamente, Oleg Yaroshevych


E se eu não souber o endereço de e-mail?
Harsimranb de

@ Shyamal-Parikh isso não funcionará para e-mails html, mas normalmente uma mensagem de texto simples também é incluída nas mensagens de e-mail
maembe

25

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.


1
Você deve fazer uma pergunta ruby ​​e respondê-la com este código em vez de postá-la em uma pergunta c #.
Matthieu,

6
@Matthieu, não é apenas uma questão C #, mas um e-mail e uma questão de análise de e-mail. totalmente relevante na minha opinião.
Trent

@Trent: a tag C # deve ser descartada então.
Matthieu

7
O engraçado é que encontrei essa pergunta pesquisando sobre o tópico (não a linguagem) e, na verdade, precisava implementar algo em Ruby. Então, saúde!
bratsche,

2
Esta é a melhor resposta até agora. Regex é bastante agnóstico quanto ao idioma. Obrigado por postar
superluminário de

11

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á.


Essa abordagem falha nas respostas às respostas, a menos que você coloque essa linha toda vez que responder.
jpw

1
Sim, tem desvantagens. Se o usuário excluir a resposta acima da string de linha, sua resposta falhará. Eu pego esse caso e envio ao usuário uma mensagem direta avisando que a mensagem falhou, com um link para responder pelo aplicativo da web. A maioria dos usuários parece conseguir usá-lo sem muitos problemas.
superluminário de

Esta deve ser a resposta aceita. No entanto, gostaria de acrescentar a informação de que a resposta não será bem-sucedida se a linha for removida.
Benni

@Benni - sim, irá falhar se a linha for removida. Infelizmente, não existe uma maneira padrão de citar texto em clientes de email. No caso em que a linha é removida, você pode tratar todo o texto como uma resposta. Não acho que uma solução perfeita seja possível neste caso.
superluminário de

@superluminary eu quis dizer, eu adicionaria isso à linha. Então é algo parecido -- 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 uma xxx 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.
Benni

6

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.


o gmail faz isso ... pelo menos parece fazer isso. Pelo que me lembro, há algum id de thread que não muda entre o original e as respostas ...
kenny

O gmail pode adicionar '>' s como fazem outros clientes de e-mail, mas não é um padrão de e-mails e não é algo com que você possa contar
3Doubloons

5

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

3

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.


0

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


Alguém pode ajudar por sua versão php?
user4271704


-1

É 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


1
Links para recursos externos são encorajados, mas por favor, adicione contexto ao link para que seus outros usuários tenham uma ideia do que é e por que está lá. Sempre cite a parte mais relevante de um link importante, caso o site de destino esteja inacessível ou fique permanentemente offline.
pableiros

você está mantendo essa biblioteca atualizada? Eu vim pesquisar porque a biblioteca C # não analisa adequadamente um e-mail simples do Outlook do Office 365. Então eu olhei no código-fonte do ruby ​​e descobri que havia um caso de teste idêntico em seus casos de teste tão claramente que eles acham que deveriam analisar isto.
Greg Veres

-1

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.

insira a descrição da imagem aqui

Você pode ver as especificações detalhadas da API aqui.

https://api.sigparser.com/

insira a descrição da imagem aqui

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.