Analyser le contenu de l'e-mail à partir de la réponse citée

87

J'essaie de comprendre comment analyser le texte d'un e-mail à partir de tout texte de réponse cité qu'il pourrait inclure. J'ai remarqué qu'en général, les clients de messagerie mettent un "À telle ou telle date telle ou telle date" ou préfixent les lignes avec un crochet angulaire. Malheureusement, tout le monde ne le fait pas. Quelqu'un at-il une idée sur la façon de détecter par programme le texte de réponse? J'utilise C # pour écrire cet analyseur.

VanOrman
la source
2
Avez-vous eu de la chance avec ça? Je cherche à faire exactement la même chose.
steve_c
une solution finale avec un échantillon de code source complet fonctionnant à ce sujet?
Kiquenet
Quotequail fait cela en Python
philfreo
Quelqu'un peut-il aider pour sa version php?
user4271704

Réponses:

60

J'ai fait beaucoup plus de recherches à ce sujet et voici ce que j'ai trouvé. Il y a essentiellement deux situations dans lesquelles vous faites cela: lorsque vous avez le fil entier et quand vous ne le faites pas. Je vais le diviser en deux catégories:

Lorsque vous avez le fil:

Si vous disposez de toute la série d'e-mails, vous pouvez obtenir un niveau d'assurance très élevé que ce que vous supprimez est en fait du texte cité. Il y a deux façons de faire ça. Premièrement, vous pouvez utiliser l'ID de message, l'ID en réponse à et l'index de thread du message pour déterminer le message individuel, son parent et le thread auquel il appartient. Pour plus d' informations sur ce sujet , voir RFC822 , RFC2822 , cet article intéressant sur le filetage , ou cet article sur le filetage . Une fois que vous avez réassemblé le fil, vous pouvez ensuite supprimer le texte externe (comme les lignes À, De, CC, etc.) et vous avez terminé.

Si les messages avec lesquels vous travaillez n'ont pas d'en-têtes, vous pouvez également utiliser la correspondance de similarité pour déterminer quelles parties d'un e-mail sont le texte de réponse. Dans ce cas, vous êtes obligé de faire une correspondance de similitude pour déterminer le texte qui est répété. Dans ce cas, vous voudrez peut-être examiner un algorithme de distance de Levenshtein tel que celui-ci sur Code Project ou celui-ci .

Quoi qu'il en soit, si vous êtes intéressé par le processus de threading, consultez cet excellent PDF sur le réassemblage des fils de courrier électronique .

Lorsque vous n'avez pas le fil:

Si vous êtes bloqué avec un seul message du fil de discussion, vous devez essayer de deviner le devis. Dans ce cas, voici les différentes méthodes de cotation que j'ai vues:

  1. une ligne (comme vu dans Outlook).
  2. Équerres
  3. "--- Message d'origine ---"
  4. "Tel et tel jour, untel a écrit:"

Supprimez le texte à partir de là et vous avez terminé. L'inconvénient de l'un d'entre eux est qu'ils supposent tous que l'expéditeur a placé sa réponse au-dessus du texte cité et ne l'a pas entrelacée (comme c'était l'ancien style sur Internet). Si cela arrive, bonne chance. J'espère que cela aidera certains d'entre vous!

VanOrman
la source
32

Tout d'abord, c'est une tâche délicate.

Vous devez collecter les réponses typiques de différents clients de messagerie et préparer des expressions régulières correctes (ou autre) pour les analyser. J'ai recueilli des réponses d'Outlook, de Thunderbird, de Gmail, d'Apple Mail et de mail.ru.

J'utilise des expressions régulières pour analyser la réponse de la manière suivante: si l'expression ne correspond pas, j'essaie d'utiliser la suivante.

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

Pour supprimer la citation à la fin:

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

Voici ma petite collection de réponses aux tests (échantillons divisés par --- ):

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

Meilleures salutations, Oleg Yaroshevych

Oleg Yaroshevych
la source
Et si je ne connais pas l'adresse e-mail?
harsimranb
@ Shyamal-Parikh cela ne fonctionnera pas pour les e-mails html, mais généralement un message en clair est également inclus avec les e-mails
maembe
25

Merci, Goleg, pour les regex! Vraiment aidé. Ce n'est pas C #, mais pour les googleurs, voici mon script d'analyse 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

Cela a plutôt bien fonctionné jusqu'à présent.

hurshagrawal
la source
1
Vous devriez faire une question ruby ​​et y répondre avec ce code au lieu de la poster sur la question ac #.
Matthieu
6
@Matthieu, ce n'est pas seulement une question C #, mais une question d'analyse des e-mails et des e-mails. totalement pertinent à mon avis.
Trent
@Trent: la balise C # doit alors être supprimée.
Matthieu
7
Le plus drôle, c'est que j'ai trouvé cette question sur Google pour le sujet (pas le langage), et j'avais en fait besoin d'implémenter quelque chose dans Ruby. Alors, bravo!
bratsche
2
C'est la meilleure réponse à ce jour. Regex est assez indépendant du langage. Merci pour l' affichage
Superluminary
11

Le moyen de loin le plus simple de le faire est de placer un marqueur dans votre contenu, tel que:

--- Veuillez répondre au-dessus de cette ligne ---

Comme vous l'avez sans doute remarqué, l'analyse du texte cité n'est pas une tâche triviale car différents clients de messagerie citent le texte de différentes manières. Pour résoudre correctement ce problème, vous devez prendre en compte et tester chaque client de messagerie.

Facebook peut le faire, mais à moins que votre projet n'ait un gros budget, vous ne pouvez probablement pas.

Oleg a résolu le problème en utilisant des expressions régulières pour trouver le texte "Le 13 juillet 2012, à 13:09, xxx a écrit:". Cependant, si l'utilisateur supprime ce texte ou répond en bas de l'e-mail, comme le font de nombreuses personnes, cette solution ne fonctionnera pas.

De même, si le client de messagerie utilise une chaîne de date différente ou n'inclut pas de chaîne de date, l'expression régulière échouera.

superluminaire
la source
Cette approche échoue avec les réponses aux réponses, sauf si vous mettez cette ligne à chaque fois que vous répondez.
jpw
1
Oui, cela présente des inconvénients. Si l'utilisateur supprime la réponse au-dessus de la chaîne de ligne, votre réponse échouera. J'attrape ce cas et envoie à l'utilisateur un message direct l'informant que son message a échoué, avec un lien pour répondre via l'application Web. La plupart des utilisateurs semblent pouvoir l'utiliser sans trop de problèmes.
superluminaire
Cela devrait être la réponse acceptée. Cependant, j'ajouterais que la réponse ne réussira pas si la ligne est supprimée.
Benni
@Benni - oui, cela échouera si la ligne est supprimée. Malheureusement, il n'existe pas de méthode standard pour citer du texte dans les clients de messagerie. Dans le cas où la ligne est supprimée, vous pouvez traiter tout le texte comme une réponse. Je ne pense pas qu'une solution parfaite soit possible dans ce cas.
superluminaire
@superluminary Je voulais dire, je l'ajouterais à la ligne. Donc c'est quelque chose comme -- Please reply above this line. DO NOT REMOVE IT! --. De plus, ce que j'ai constaté, c'est que cela ne fonctionnera pas toujours puisque certains clients de messagerie ajoutent une xxx wrote on <datetime>:ligne avant le devis entier et donc avant cette ligne. Cette ligne peut être analysée avec regex, mais elle peut être dans différentes langues et dans un format différent puisque les clients de messagerie diffèrent.
Benni
6

Il n'y a pas d'indicateur universel de réponse dans un e-mail. Le mieux que vous puissiez faire est d'essayer d'attraper les plus courants et d'analyser les nouveaux modèles au fur et à mesure que vous les rencontrez.

Gardez à l'esprit que certaines personnes insèrent des réponses dans le texte cité (mon patron par exemple répond aux questions sur la même ligne que je leur ai posé) donc quoi que vous fassiez, vous risquez de perdre des informations que vous auriez aimé conserver.

3Doublons
la source
gmail le fait ... du moins il semble le faire. D'après ce dont je me souviens, il y a un identifiant de fil qui ne change pas entre l'original et les réponses ...
kenny
gmail peut ajouter des '>' comme le font d'autres clients de messagerie, mais ce n'est pas un standard d'e-mails et pas quelque chose sur
lequel
5

Voici ma version C # du code Ruby de @ hurshagrawal. Je ne connais pas très bien Ruby donc ça pourrait être faux, mais je pense que j'ai bien compris.

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();
}
Austin
la source
3

Si vous contrôlez le message d'origine (par exemple, les notifications d'une application Web), vous pouvez mettre en place un en-tête distinct et identifiable et l'utiliser comme délimiteur pour le message d'origine.

Eric R. Rath
la source
0

C'est une bonne solution. Je l'ai trouvé après avoir cherché si longtemps.

Un ajout, comme mentionné ci-dessus, c'est casse, donc les expressions ci-dessus n'ont pas analysé correctement mes réponses gmail et outlook (2010), pour lesquelles j'ai ajouté les deux expressions régulières suivantes. Faites-moi savoir pour tout problème.

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

À votre santé

Amit M
la source
Quelqu'un peut-il aider pour sa version php?
user4271704
-1

C'est un ancien message, cependant, je ne sais pas si vous savez que github a une librairie Ruby extrayant la réponse. Si vous utilisez .NET, j'en ai un .NET sur https://github.com/EricJWHuang/EmailReplyParser

Eric Huang
la source
1
Les liens vers des ressources externes sont encouragés, mais veuillez ajouter du contexte autour du lien afin que vos collègues utilisateurs aient une idée de ce que c'est et pourquoi il est là. Citez toujours la partie la plus pertinente d'un lien important, au cas où le site cible serait inaccessible ou serait définitivement hors ligne.
pableiros
gardez-vous cette bibliothèque à jour? Je suis venu chercher parce que la bibliothèque C # n'analyse pas correctement un simple e-mail d'Outlook à partir d'Office 365. Ensuite, j'ai regardé dans le code source ruby ​​et j'ai trouvé qu'il y avait un cas de test identique dans leurs cas de test si clairement qu'ils pensent qu'ils devraient analyser il.
Greg Veres
-1

Si vous utilisez l'API de SigParser.com , il vous donnera un tableau de tous les e-mails éclatés dans une chaîne de réponse à partir d'une seule chaîne de texte d'e-mail. Donc, s'il y a 10 e-mails, vous obtiendrez le texte des 10 e-mails.

entrez la description de l'image ici

Vous pouvez consulter les spécifications détaillées de l'API ici.

https://api.sigparser.com/

entrez la description de l'image ici

Paul Mendoza
la source