Enviar y leer email con python y gmail

De ChuWiki

Enviar y leer correos con Phyton y gmail[editar]

Vamos a ver aquí unos ejemplos de cómo enviar un correo simple con python y gmail, otro para un correo con una imagen adjunta y otro sobre cómo leer dichos correos. Aquí iremos viendo y explicando los distintos trozos del código python. En los enlaces tienes los fuentes completos.

Envío de correos con gmail[editar]

Vamos con la parte de enviar correos. Puesto que la única diferencia entre el correo normal y el correo con adjunto es la forma de construir el mensaje, explicaremos aquí conjuntamente ambos ejemplos de envío de correo.


Establecer la conexión con smtp.gmail.com[editar]

Para establecer la conexión, usaremos smtplib.SMT. La forma de establecer la conexión es la siguiente

# Establecemos conexion con el servidor smtp de gmail
mailServer = smtplib.SMTP('smtp.gmail.com',587)
mailServer.ehlo()
mailServer.starttls()
mailServer.ehlo()
mailServer.login("usuario@gmail.com","password")

Básicamente indicamos el nombre del servidor de correo smtp.gmail.com y el puerto de conexión 587. Luego establecemos el protocolo starttls(), enviando previamente un ehlo() para que nos lo acepte. Finalmente entramos en sesión con el usuario@gmail.com y la password correspondiente, enviando nuevamente antes un ehlo().

Si realizásemos este código correctamente, cada una de esas llamadas ehlo(), starttls() y login() devuelven un texto con el resultado de la llamada, que deberíamos comprobar antes de seguir adelante. Poniendo delante un print puedes ver el tipo de texto que es

 ...
 print mailServer.ehlo()
 ...

Construir el mensaje a enviar[editar]

Para construir el mensaje, se usan cosas como email.MIMEMultipart, email.MIMEText, email.MIMEImage, etc. Si nuestro mensaje es simple, usaremos un MIMEText. Si es compuesto, con adjuntos, usaremos un MIMEMultipart y le iremos añadiendo MIMEText, MIMEImage o lo que sea.

Para construir un MIMEText, el código puede ser el siguiente

# Construimos el mensaje simple
mensaje = MIMEText("""Este es el mensaje
de las narices""")
mensaje['From']="usuario@gmail.com"
mensaje['To']="destinatario@gmail.com"
mensaje['Subject']="Tienes un correo"

Hemos instanciado un MIMEText pasándole en el constructor el texto del mensaje. Luego hemos rellenado los From, To y Subject de nuestro mensaje. Con esto estaría listo un mensaje simple para enviar.

# Envio del mensaje
mailServer.sendmail("usuario@gmail.com",
                "destinatario@gmail.com",
                mensaje.as_string())


Construir un mensaje compuesto[editar]

Si queremos el mensaje con adjuntos, primero creamos un MIMEMultipart, que hará de contenedor de todo lo demás.

# Construimos un mensaje Multipart, con un texto y una imagen adjunta
mensaje = MIMEMultipart()
mensaje['From']="usuario@gmail.com"
mensaje['To']="destinatario@gmail.com"
mensaje['Subject']="Tienes un correo"

De momento es sencillo. Simplemente hemos instanciado un MIMEMultipart y hemos rellenado, igual que antes, los From, To y Subject. Vamos ahora a meterle contenido. Primero, podemos meter un mensaje de texto. Basta llamar al método attach() pasándole el MIMEText en cuestión, que instanciamos sobre la marcha.

# Adjuntamos el texto
mensaje.attach(MIMEText("""Este es el mensaje
de las narices"""))

Y ahora podemos construir un MIMEImage para adjuntar la imagen también y, por supuesto, la adjuntamos

# Adjuntamos la imagen
file = open("Chuidiang-64.gif", "rb")
contenido = MIMEImage(file.read())
contenido.add_header('Content-Disposition', 'attachment; filename = "Chuidiang-64.gif"')
mensaje.attach(contenido)

Hemos abierto un fichero de imagen Chuidang-64.gif. Creamos el MIMEImage pasándole en el constructor la lectura del fichero. Añadimos a la cabecera toda esa ristra tan rara de Content-Disposition y attachment; filename para indicar el nombre del fichero adjunto y que el receptor sepa cómo se llama el fichero. Esa ristra rara está definida en Multipurpose Internet Mail Extesions (MIME). Finalmente hemos añadido el MIMEImage al MIMEMultipart anterior con el método attach(). Ya solo queda enviar el mensaje y cerrar la conexión con el servidor smtp.

# Enviamos el correo, con los campos from y to.
mailServer.sendmail("usuario@gmail.com",
                "destinatario@gmail.com",
                mensaje.as_string())

# Cierre de la conexion
mailServer.close()

Leer correo de gmail[editar]

Establecer la conexión[editar]

Para la lectura, primero establecemos la conexión con el servidor pop de gmail

# Se establece conexion con el servidor pop de gmail
m = poplib.POP3_SSL('pop.gmail.com',995)
m.user('usuario@gmail.com')
m.pass_('password')

Hemos usado poplib.POP3_SSL, pasando el nombre del servidor pop.gmail.com y el puerto de conexión 995. Luego hemos fijado el usuario y password de gmail. Por cierto, el método es pass_(), con un subrayado al final.


Mirar cuántos mensajes hay sin leer[editar]

Para ver cuántos mensajes quedan pendientes de lectura, llamamos al método list(), que nos devuelve un (response, ['mesg_num octets', ...], octets). La forma de obtener de ahí el número de mensajes es

 numero = len(m.list()[1])

Con esto, sólo tenemos que hacer un bucle e ir obteniendo los mensajes de uno en uno para analizarlos

for i in range (numero):
   print "Mensaje numero"+str(i+1)
   print "--------------------"
   # Se lee el mensaje
   response, headerLines, bytes = m.retr(i+1)

El método retr(i+1) trae del servidor el mensaje cuyo número se indica y lo marca en el servidor como leído. Se pone i+1 porque el método retr() comienza en 1 y no en cero. Este método devuelve la respuesta del servidor, el mensaje y unos bytes que no he encontrado en ningún sitio que demonios son. Lo importante es el headerlines, que de alguna forma contiene todas las líneas del mensaje.


Obtener el contenido del mensaje[editar]

Para obtener el contenido del mensaje que actualmente está en headerLines, usaremos la clase email.Parser. Esta clase es capaz de devolvernos una clase email.message.Message, que es el mensaje ya convertido en algo tratable. El problema es que Parser necesita un único string todo seguido con el mensaje y no un headerLines, que son varios strings. Por ello, debemos juntar todos esos strings en uno solo

 # Se mete todo el mensaje en un unico string
 mensaje='\n'.join(headerLines)

y ahora ya sí podemos obtener el mensaje en condiciones

 # Se parsea el mensaje
 p = Parser()
 email = p.parsestr(mensaje)

y listo. Ya solo es cuestión de ir llamando a los métodos de la clase Message para ir sacando el contenido. Lo primero que podemos hacer es obtener los campos From, To y Subject.

 # Se sacan por pantalla los campos from, to y subject
 print "From: "+email["From"]
 print "To: "+email["To"]
 print "Subject: "+email["Subject"]

Puede ser útil obtener el identificador ID del correo con

 print "ID: "+email['message-id']

Luego podemos ver si es un mensaje Multipart (con adjuntos), o es uno simple. Para ello usamos el método is_multipart() y en función de eso, iremos extrayendo los contenidos

 # Si es un mensaje compuesto
 if (email.is_multipart()):

Supongamos que no es Multipart. Podemos mirar el tipo de contenido y si es de texto, extraerlo

tipo = email.get_content_type()
if ("text/plain" == tipo):
   # Si es texto plano, se escribe en pantalla
   print email.get_payload(decode=True)

Obtenemos el tipo con get_content_type() y vamos comprobando si es "text/plain" o los que queramos. Tenemos también get_content_maintype() y get_content_subtype() para obtener sólo la parte de "text" o la de "plain", de forma que podemos afinar más o tratar varios tipos de "text" de golpe. Una vez que identificamos que el tipo es de texto, obtenemos el contenido llamando a get_payload() indicando que nos lo dé decodificado.

Si fuese de imagen, el tratamiento sería como el siguiente

if ("image/gif" == tipo):
   # Si es imagen, se extrae el nombre del fichero
   # adjunto y se guarda la imagen
   nombre_fichero = email.get_filename()
   fp = open(nombre_fichero,'wb')
   fp.write(part.get_payload(decode=True))
   fp.close()

Si el tipo es de imagen, con get_filename() obtenemos el nombre del fichero de imagen. Abrimos ese fichero para escritura binaria (wb), y escribimos en él el contenido del mensaje get_payload() ya decodificado. Finalmente cerramos el fichero

Si el mensaje hubiese sido multipart, habría que extraer las distintas partes en un bucle

   if (email.is_multipart()):
      # bucle para cada parte del mensaje
      for part in email.get_payload():

Ahora cada part extraída se trata exactamente que los casos anteriores, con su get_content_type().

Finalmente, cerramos la conexión con el servidor pop de gmail.

 # Cierre de la conexion
 m.quit()

Es necesario este cerrado si queremos que la marca de los mensajes como leídos en el servidor ,que puso el método retr(), se haga efectiva.

Tienes todos los códigos completos de estos ejemplos en python-gmail

Enlaces[editar]