Analizar el contenido del correo electrónico de la respuesta citada

86

Estoy tratando de averiguar cómo analizar el texto de un correo electrónico a partir de cualquier texto de respuesta citado que pueda incluir. He notado que, por lo general, los clientes de correo electrónico colocarán un "En tal o cual fecha tal y tal escribió" o prefijarán las líneas con un corchete angular. Desafortunadamente, no todo el mundo hace esto. ¿Alguien tiene alguna idea sobre cómo detectar mediante programación el texto de respuesta? Estoy usando C # para escribir este analizador.

VanOrman
fuente
2
¿Tuviste suerte con esto? Estoy buscando hacer exactamente lo mismo.
steve_c
¿Alguna solución final con una muestra de código fuente completa que funcione?
Kiquenet
Quotequail hace esto en Python
philfreo
¿Alguien puede ayudar con su versión php?
user4271704

Respuestas:

60

Hice muchas más búsquedas sobre esto y esto es lo que encontré. Básicamente, hay dos situaciones en las que está haciendo esto: cuando tiene el hilo completo y cuando no lo tiene. Lo dividiré en esas dos categorías:

Cuando tienes el hilo:

Si tiene toda la serie de correos electrónicos, puede lograr un alto nivel de seguridad de que lo que está eliminando es en realidad texto citado. Hay dos maneras de hacer esto. Uno, puede usar el ID de mensaje, el ID de respuesta a, y el índice de subproceso del mensaje para determinar el mensaje individual, su padre y el subproceso al que pertenece. Para obtener más información sobre esto, consulte RFC822 , RFC2822 , este interesante artículo sobre subprocesos o este artículo sobre subprocesos . Una vez que haya vuelto a ensamblar el hilo, puede eliminar el texto externo (como Para, De, CC, etc ... líneas) y listo.

Si los mensajes con los que está trabajando no tienen los encabezados, también puede usar la coincidencia de similitudes para determinar qué partes de un correo electrónico son el texto de respuesta. En este caso, tiene que hacer coincidir similitudes para determinar el texto que se repite. En este caso, es posible que desee buscar un algoritmo de distancia de Levenshtein como este en Code Project o este .

Pase lo que pase, si está interesado en el proceso de subprocesamiento, consulte este excelente PDF sobre cómo volver a ensamblar los subprocesos de correo electrónico .

Cuando no tienes el hilo:

Si está atascado con un solo mensaje del hilo, debe intentar adivinar cuál es la cita. En ese caso, aquí están los diferentes métodos de cotización que he visto:

  1. una línea (como se ve en Outlook).
  2. Paréntesis angulares
  3. "---Mensaje original---"
  4. "En tal y tal día, tal y tal escribió:"

Elimina el texto de allí hacia abajo y listo. La desventaja de cualquiera de estos es que todos asumen que el remitente puso su respuesta encima del texto citado y no lo intercalo (como era el estilo antiguo en Internet). Si eso sucede, buena suerte. ¡Espero que esto ayude a algunos de ustedes!

VanOrman
fuente
32

Primero que nada, esta es una tarea complicada.

Debe recopilar respuestas típicas de diferentes clientes de correo electrónico y preparar expresiones regulares correctas (o lo que sea) para analizarlas. He recopilado respuestas de outlook, thunderbird, gmail, apple mail y mail.ru.

Estoy usando expresiones regulares para analizar la respuesta de la siguiente manera: si la expresión no coincide, trato de usar la siguiente.

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 eliminar la cotización al final:

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

Aquí está mi pequeña colección de respuestas de prueba (muestras 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

Saludos cordiales, Oleg Yaroshevych

Oleg Yaroshevych
fuente
¿Qué pasa si no conozco la dirección de correo electrónico?
harsimranb
@ Shyamal-Parikh esto no funciona para mensajes de correo electrónico HTML, pero por lo general un mensaje de texto plano también se incluye con los correos electrónicos
Maembe
25

¡Gracias, Goleg, por las expresiones regulares! Realmente ayudó. Esto no es C #, pero para los googlers, aquí está mi script de análisis de 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

Ha funcionado bastante bien hasta ahora.

hurshagrawal
fuente
1
Debe hacer una pregunta ruby ​​y responderla con este código en lugar de publicarla en ac # question.
Matthieu
6
@Matthieu, no es solo una pregunta de C #, sino una pregunta de correo electrónico y de análisis de correo electrónico. totalmente relevante en mi opinión.
Trent
@Trent: la etiqueta C # debe eliminarse entonces.
Matthieu
7
Lo curioso es que encontré esta pregunta buscando en Google el tema (no el idioma), y en realidad necesitaba implementar algo en Ruby. ¡Salud!
bratsche
2
Esta es la mejor respuesta hasta ahora. Regex es bastante independiente del lenguaje. Gracias por publicar
superluminario
11

De lejos, la forma más fácil de hacerlo es colocando un marcador en su contenido, como:

--- Responda por encima de esta línea ---

Como sin duda habrá notado, analizar el texto citado no es una tarea trivial ya que diferentes clientes de correo electrónico citan el texto de diferentes maneras. Para resolver este problema correctamente, debe contabilizar y probar en cada cliente de correo electrónico.

Facebook puede hacer esto, pero a menos que su proyecto tenga un gran presupuesto, probablemente no pueda.

Oleg ha resuelto el problema usando expresiones regulares para encontrar el texto "El 13 de julio de 2012, a las 13:09, xxx escribió:". Sin embargo, si el usuario elimina este texto o responde al final del correo electrónico, como hacen muchas personas, esta solución no funcionará.

Del mismo modo, si el cliente de correo electrónico usa una cadena de fecha diferente o no incluye una cadena de fecha, la expresión regular fallará.

superluminario
fuente
Este enfoque falla con las respuestas a las respuestas a menos que ponga esa línea cada vez que responde.
jpw
1
Sí, tiene inconvenientes. Si el usuario elimina la respuesta por encima de la cadena de línea, su respuesta fallará. Detecto este caso y le envío al usuario un mensaje directo para informarle que su mensaje falló, con un enlace para responder a través de la aplicación web. La mayoría de los usuarios parecen poder usarlo sin demasiados problemas.
superluminario
Esta debería ser la respuesta aceptada. Sin embargo, agregaría la información de que la respuesta no tendrá éxito si se elimina la línea.
Benni
@Benni: sí, fallará si se elimina la línea. Desafortunadamente, no existe una forma estándar de citar texto en los clientes de correo electrónico. En el caso de que se elimine la línea, puede tratar todo el texto como una respuesta. No creo que en este caso sea posible una solución perfecta.
superluminario
@superluminary quise decir, lo agregaría a la línea. Entonces es algo así como -- Please reply above this line. DO NOT REMOVE IT! --. Además, lo que experimenté es que no siempre funcionará, ya que algunos clientes de correo electrónico agregan una xxx wrote on <datetime>:línea antes de toda la cotización y, por lo tanto, antes de esa línea. Esta línea se puede analizar con expresiones regulares, sin embargo, puede estar en diferentes idiomas y en un formato diferente, ya que los clientes de correo electrónico son diferentes.
Benni
6

No existe un indicador universal de una respuesta en un correo electrónico. Lo mejor que puede hacer es intentar captar los patrones más comunes y analizar nuevos patrones a medida que los encuentre.

Tenga en cuenta que algunas personas insertan respuestas dentro del texto citado (mi jefe, por ejemplo, responde las preguntas en la misma línea que yo les hice), así que, hagas lo que hagas, es posible que pierdas información que te hubiera gustado conservar.

3Doblones
fuente
gmail lo hace ... al menos parece que lo hace. Por lo que recuerdo, hay una identificación de hilo que no cambia entre el original y las respuestas ...
kenny
gmail puede agregar '>' al igual que otros clientes de correo electrónico, pero no es un estándar de correos electrónicos y no es algo con lo que pueda contar
3Doubloons
5

Aquí está mi versión C # del código Ruby de @ hurshagrawal. No conozco muy bien a Ruby, así que podría fallar, pero creo que lo hice bien.

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
fuente
3

Si controlas el mensaje original (por ejemplo, notificaciones de una aplicación web), puedes colocar un encabezado identificable y distinto en su lugar y usarlo como delimitador de la publicación original.

Eric R. Rath
fuente
0

Esta es una buena solución. Lo encontré después de buscar durante tanto tiempo.

Una adición, como se mencionó anteriormente, esto es sabio, por lo que las expresiones anteriores no analizaron correctamente mis respuestas de Gmail y Outlook (2010), para lo cual agregué las siguientes dos Regex (s). Avísame si tienes algún 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),

Salud

Amit M
fuente
¿Alguien puede ayudar con su versión php?
user4271704
-1

Es una publicación antigua, sin embargo, no estoy seguro de si sabe que github tiene una lib de Ruby extrayendo la respuesta. Si usa .NET, tengo uno .NET en https://github.com/EricJWHuang/EmailReplyParser

Eric Huang
fuente
1
Se recomiendan los enlaces a recursos externos, pero agregue contexto alrededor del enlace para que sus compañeros usuarios tengan una idea de qué es y por qué está allí. Siempre cite la parte más relevante de un enlace importante, en caso de que el sitio de destino sea inaccesible o se desconecte permanentemente.
pableiros
¿Mantienes esa biblioteca actualizada? Vine a buscar porque la biblioteca de C # no analiza correctamente un simple correo electrónico de Outlook desde Office 365. Luego miré en el código fuente ruby ​​y descubrí que había un caso de prueba idéntico en sus casos de prueba tan claramente que ellos piensan que deberían analizar eso.
Greg Veres
-1

Si usa la API de SigParser.com , le dará una serie de todos los correos electrónicos desglosados ​​en una cadena de respuesta a partir de una sola cadena de texto de correo electrónico. Entonces, si hay 10 correos electrónicos, obtendrá el texto de los 10 correos electrónicos.

ingrese la descripción de la imagen aquí

Puede ver las especificaciones detalladas de la API aquí.

https://api.sigparser.com/

ingrese la descripción de la imagen aquí

Pablo Mendoza
fuente