Enviar emails con python y gmail sin instalar paquetes extras

Enviar emails con python y gmail sin instalar paquetes extras

Enviar emails usando las librerías de python: smtplib y email

Para poder enviar emails desde gmail ya sea con python o con cualquier otro lenguaje hay que habilitar el acceso a la cuenta desde clientes externos.

Esto se hace iniciando sesión en la cuenta y después yendo a este enlace y activando el acceso a Permitir el acceso de aplicaciones poco seguras
Nota: si estas usando factor de doble autenticación no te permitirá habilitar esta opción

Con gmail configurado, supondremos que estamos usando la cuenta some-from@gmail.com y contraseña :)Abcd1234

Esta es la distribución de los tres archivos para la prueba:

email/ ├── example-1.txt (para adjuntar) ├── example-2.txt └── main.py (donde está toda la lógica de la función send_email)

Se ejecuta así:

# en mi caso estoy usando python 3.6 o superior python main.py

Como se puede observar en la imagen la composición lleva texto plano, html y dos adjuntos algo muy común en los procesos de mailing.
Realmente esta librería de python ofrece más opciones que podeis revisar en la documentación.

El código (main.py):

from typing import List """ Los datos de acceso a la cuenta de gmail que se usará para enviar el email """ def get_gmail_login() -> dict: return { "user": "some-from@gmail.com", "pwd": ":)Abcd1234" } """ Lista de destinatarios """ def get_cc_recipients() -> List[str]: return [ "to-email-1@gmail.com", "to-email-2@yahoo.es", ] """ Lista de destinatarios ocultos """ def get_bcc_recipients() -> List[str]: return [ "to-email-bcc@hotmail.com" ] """ Ruta de los archivos a adjuntar """ def get_attachments() -> List[str]: return [ "./example-1.txt", "./example-2.txt" ] def get_html_template() -> str: html= """ <html> <head> <style> .body-email { margin:0; padding:0; } .img { margiin:0; padding:0; width: 350px; height: auto; } .table-email { width: 100%; border: 1px solid #FEAB52; border-spacing: 10px; border-collapse: separate; } .p { margin:0; padding: 14px; text-align: justify; color: white; font-size: 15px; background-color: #FE9A3E; } .td { text-align: center; } .h1 { text-decoration: underline; } </style> </head> <body class="body-email"> <center> <table class="table-email"> <tr> <td class="td"> <img src="https://resources.theframework.es/eduardoaf.com/20200906/095050-logo-eduardoafcom_500.png" class="img"/> <h1 class="h1"> Example H1 </h1> <p class="p"> Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. </p> </td> </tr> </table> </center> </body> </html> """ return html """ Crea la conexión con gmail """ def get_smtpssl_object(): import smtplib login = get_gmail_login() server_ssl = smtplib.SMTP_SSL("smtp.gmail.com", 465) server_ssl.ehlo() server_ssl.login(login["user"], login["pwd"]) return server_ssl def send_email() -> None: from pathlib import Path from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.base import MIMEBase from email import encoders # MIME: Multipurpose Internet Mail Extensions. Mp3, Mid, mpeg, mov, nws etc # mixed permite el envío de html y plain mime_obj = MIMEMultipart("mixed") subject = "This is an email subject from Python (example 1)" mime_obj["Subject"] = subject #mime_obj["From"] = "loquesea@mimail.com" no tiene ningún efecto # esto podría ir en blanco y llegaría pero el cliente de correo no mostraría nada en destinatarios # el o los receptores es decir irian como Bcc mime_obj["To"] = ", ".join(get_cc_recipients()) plain_text = "This is a plain text example" mime_plain = MIMEText(plain_text, "plain") html = get_html_template() mime_html = MIMEText(html, "html") mime_obj.attach(mime_html) mime_obj.attach(mime_plain) smtp_obj = get_smtpssl_object() smtp_obj.set_debuglevel(1) # tratando los adjuntos (examples.txt) for path in get_attachments(): part = MIMEBase('application', "octet-stream") with open(path, 'rb') as file: part.set_payload(file.read()) encoders.encode_base64(part) filename = Path(path).name part.add_header('Content-Disposition',f'attachment; filename="{filename}"') mime_obj.attach(part) smtp_obj.sendmail( #si esto se deja en blanco también se envía. No da error pero llega a SPAM. #No es necesario que coincida con el email de la cuenta de gmail "anyaccount@somedomain.local", get_cc_recipients()+get_bcc_recipients(), mime_obj.as_string() ) smtp_obj.close() if __name__ == "__main__": try: send_email() except Exception as ex: print("Unexpected error: ",str(ex))

Imágenes

Como es habitual dejo el código fuente en mi github

Trazas del envío:

send: 'mail FROM:<anyaccount@somedomain.local> size=4644\r\n' reply: b'250 2.1.0 OK p17sm7901123wmq.47 - gsmtp\r\n' reply: retcode (250); Msg: b'2.1.0 OK p17sm7901123wmq.47 - gsmtp' send: 'rcpt TO:<to-email-1@gmail.com>\r\n' reply: b'250 2.1.5 OK p17sm7901123wmq.47 - gsmtp\r\n' reply: retcode (250); Msg: b'2.1.5 OK p17sm7901123wmq.47 - gsmtp' send: 'rcpt TO:<to-email-2@yahoo.es>\r\n' reply: b'250 2.1.5 OK p17sm7901123wmq.47 - gsmtp\r\n' reply: retcode (250); Msg: b'2.1.5 OK p17sm7901123wmq.47 - gsmtp' send: 'rcpt TO:<to-email-bcc@hotmail.com>\r\n' reply: b'250 2.1.5 OK p17sm7901123wmq.47 - gsmtp\r\n' reply: retcode (250); Msg: b'2.1.5 OK p17sm7901123wmq.47 - gsmtp' send: 'data\r\n' reply: b'354 Go ahead p17sm7901123wmq.47 - gsmtp\r\n' reply: retcode (354); Msg: b'Go ahead p17sm7901123wmq.47 - gsmtp' data: (354, b'Go ahead p17sm7901123wmq.47 - gsmtp') send: b'Content-Type: multipart/mixed; boundary="===============8147969698485163111=="\r\n MIME-Version: 1.0\r\nSubject: This is an email subject from Python (example 1)\r\n To: to-email-1@gmail.com, to-email-2@yahoo.es\r\n --===============8147969698485163111==\r\n Content-Type: text/html; charset="us-ascii"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\n r\n\r\n <html> <head> <style> .body-email { margin:0; padding:0; } .img { margiin:0; padding:0; width: 350px; height: auto; } .table-email { width: 100%; border: 1px solid #FEAB52; border-spacing: 10px; border-collapse: separate; } .p { margin:0; padding: 14px; text-align: justify; color: white; font-size: 15px; background-color: #FE9A3E; } .td { text-align: center; } .h1 { text-decoration: underline; } </style> </head> <body class="body-email"> <center> <table class="table-email"> <tr> <td class="td"> <img src="https://resources.theframework.es/eduardoaf.com/20200906/095050-logo-eduardoafcom_500.png" class="img"/> <h1 class="h1"> Example H1 </h1> <p class="p"> Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. </p> </td> </tr> </table> </center> </body> </html> --===============8147969698485163111==\r\n Content-Type: text/plain; charset="us-ascii"\r\n MIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\n \r\nThis is a plain text example\r\n --===============8147969698485163111==\r\n Content-Type: application/octet-stream\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename="example-1.txt"\r\n\r\nT05FCgpBdCB2ZXJvIGV vcyBldCBhY2N1c2FtdXMgZXQgaXVzdG8gb2RpbyBkaWduaXNzaW1vcyBk\r\n dWNpbXVzIHF1aSBibGFuZGl0aWlzIHByYWVzZW50aXVtIHZvbHVwdGF0dW0gZGVsZW5pdGkgYXRx\r\ndWUKY29ycnVwdG ... VzLCB1dCBhdXQgcmVpY2llbmRpcyB2b2x1cHRhdGlidXMgbWFpb3JlcyBhbGlhcyBjb25zZXF1\r\nYXR1ciBhdXQgcGVyZmVyZW5kaXMgZG9sb3JpYnVzCmFzcGVyaW9yZXMgcmVwZWxsYXQK\r\n --===============8147969698485163111==\r\n Content-Type: application/octet-stream\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename="example-2.txt"\r\n FdPCgpCdXQgSSBtdXN0IGV4cGxhaW4gdG8geW91IGhvdyBhbGwgdGhpcyBtaXN0YWtlbiBpZGVh ... YXN1cmUgcmF0aW9uYWxseSBlbmNvdW50ZXIgY29uc2VxdWVuY2UK\r\n\r\n --===============8147969698485163111==--\r\n.\r\n' reply: b'250 2.0.0 OK 1618069879 p17sm7901123wmq.47 - gsmtp\r\n' reply: retcode (250); Msg: b'2.0.0 OK 1618069879 p17sm7901123wmq.47 - gsmtp' data: (250, b'2.0.0 OK 1618069879 p17sm7901123wmq.47 - gsmtp')

Autor: Eduardo A. F.
Publicado: 10-04-2021 19:33