Cómo automatizar correos electrónicos con Python

Publicado el: 11/07/2026
Tempo de leitura: 4 minutos
Python e ícone de e-mail sobre teclado de notebook

Automatizar correos con Python permite enviar informes, alertas, facturas, recordatorios y mensajes personalizados sin repetir tareas manuales. La biblioteca estándar incluye smtplib para comunicarse con servidores SMTP y el paquete email para construir mensajes con texto, HTML y archivos adjuntos.

Para complementar este tutorial, consulta las guías sobre entornos virtuales, archivos, diccionarios, logging y automatización de tareas. Como referencias externas, revisa la documentación oficial del paquete email y la documentación oficial de smtplib.

Cómo funciona el envío

Tu script no entrega directamente el correo al destinatario. Se conecta a un servidor SMTP, se autentica y le entrega el mensaje. El proveedor se encarga de la transmisión posterior. Necesitas conocer el host, el puerto y el método de seguridad de tu servicio.

No guardar contraseñas en el código

Las credenciales deben permanecer fuera del archivo:

import os

USUARIO = os.environ["SMTP_USUARIO"]
CONTRASENA = os.environ["SMTP_CONTRASENA"]

Configura variables de entorno o utiliza un gestor de secretos. Añadir una contraseña directamente al repositorio puede comprometer la cuenta incluso si después borras la línea.

Crear un mensaje simple

from email.message import EmailMessage

mensaje = EmailMessage()
mensaje["Subject"] = "Informe semanal"
mensaje["From"] = "[email protected]"
mensaje["To"] = "[email protected]"
mensaje.set_content("El informe semanal está listo.")

EmailMessage administra cabeceras, cuerpo y adjuntos de forma más segura que construir el texto SMTP manualmente.

Enviar mediante SSL

import smtplib
import ssl

contexto = ssl.create_default_context()

with smtplib.SMTP_SSL("smtp.example.com", 465, context=contexto) as servidor:
    servidor.login(USUARIO, CONTRASENA)
    servidor.send_message(mensaje)

Los valores dependen del proveedor. No desactives la verificación TLS para resolver errores de certificados.

Enviar mediante STARTTLS

with smtplib.SMTP("smtp.example.com", 587) as servidor:
    servidor.ehlo()
    servidor.starttls(context=contexto)
    servidor.ehlo()
    servidor.login(USUARIO, CONTRASENA)
    servidor.send_message(mensaje)

STARTTLS inicia una conexión normal y después la protege. Utiliza el método indicado por el proveedor.

Contenido HTML con alternativa de texto

mensaje.set_content("Tu navegador de correo no muestra HTML. Total: 125,00 €")

mensaje.add_alternative("""
<h1>Informe</h1>
<p>El total procesado fue <strong>125,00 €</strong>.</p>
""", subtype="html")

Incluye siempre una versión de texto. Mejora compatibilidad, accesibilidad y entrega.

Añadir un archivo adjunto

from pathlib import Path

ruta = Path("informe.pdf")
contenido = ruta.read_bytes()

mensaje.add_attachment(
    contenido,
    maintype="application",
    subtype="pdf",
    filename=ruta.name,
)

Comprueba que el archivo exista, tenga un tamaño razonable y pertenezca al envío correcto.

Detectar el tipo MIME

import mimetypes

mime, _ = mimetypes.guess_type(ruta.name)
maintype, subtype = (mime or "application/octet-stream").split("/", 1)

mensaje.add_attachment(
    ruta.read_bytes(),
    maintype=maintype,
    subtype=subtype,
    filename=ruta.name,
)

El tipo genérico funciona cuando no se reconoce la extensión.

Varios destinatarios

destinatarios = ["[email protected]", "[email protected]"]
mensaje["To"] = ", ".join(destinatarios)

Para listas privadas, no expongas las direcciones en To. Envía mensajes individuales o utiliza BCC con cuidado. Los mensajes individuales permiten personalización y mejor control de errores.

CC y BCC

mensaje["Cc"] = "[email protected]"
bcc = ["[email protected]"]

todos = destinatarios + [mensaje["Cc"]] + bcc
servidor.send_message(mensaje, to_addrs=todos)

BCC no debe aparecer como cabecera visible. La lista se entrega mediante to_addrs.

Personalizar mensajes

PLANTILLA = "Hola {nombre}, tu pedido {pedido} está listo."

for cliente in clientes:
    cuerpo = PLANTILLA.format(
        nombre=cliente["nombre"],
        pedido=cliente["pedido"],
    )

Valida que los campos existan y escapa valores antes de insertarlos en HTML. No permitas que datos externos modifiquen cabeceras.

Crear una función reutilizable

def crear_mensaje(remitente, destinatario, asunto, texto, html=None):
    mensaje = EmailMessage()
    mensaje["From"] = remitente
    mensaje["To"] = destinatario
    mensaje["Subject"] = asunto
    mensaje.set_content(texto)

    if html:
        mensaje.add_alternative(html, subtype="html")

    return mensaje

Separar creación y envío facilita las pruebas. Puedes comprobar cabeceras y cuerpo sin conectarte a un servidor.

Manejo de errores

import logging
import smtplib

try:
    with smtplib.SMTP_SSL(HOST, PUERTO, context=contexto) as servidor:
        servidor.login(USUARIO, CONTRASENA)
        servidor.send_message(mensaje)
except smtplib.SMTPAuthenticationError:
    logging.exception("Credenciales SMTP rechazadas")
except smtplib.SMTPRecipientsRefused:
    logging.exception("Destinatario rechazado")
except (smtplib.SMTPException, OSError):
    logging.exception("No fue posible enviar el correo")

Captura excepciones específicas y registra contexto suficiente, pero nunca credenciales ni el contenido completo de mensajes sensibles.

Reintentos responsables

Reintenta solo errores temporales, con espera creciente y límite:

import time


def enviar_con_reintentos(enviar, intentos=3):
    for numero in range(1, intentos + 1):
        try:
            return enviar()
        except (smtplib.SMTPServerDisconnected, TimeoutError, OSError):
            if numero == intentos:
                raise
            time.sleep(numero * 3)

No reintentes indefinidamente una autenticación incorrecta o una dirección rechazada.

Evitar envíos duplicados

Guarda un identificador por tarea, destinatario y periodo. Antes de enviar, comprueba si ya existe un registro exitoso. Después marca el envío únicamente cuando el servidor lo haya aceptado. En sistemas críticos, utiliza una cola con estados como pendiente, procesando, enviado y fallido.

Programar el script

En Windows puedes usar el Programador de tareas. En Linux o macOS, cron es una opción habitual. También puedes ejecutar el proceso desde un servidor, contenedor o sistema de colas. La programación debe llamar al script; no es necesario mantener un while True activo.

Límites del proveedor

Los servicios aplican límites de destinatarios y frecuencia. Un script que envía miles de mensajes rápidamente puede bloquear la cuenta. Para campañas, utiliza una plataforma de correo transaccional o marketing con gestión de bajas, reputación y devoluciones.

Consentimiento y privacidad

Envía mensajes únicamente a destinatarios que los esperan. Incluye mecanismos apropiados de cancelación cuando corresponda y almacena solo los datos necesarios. No uses este tipo de automatización para correo masivo no solicitado.

Probar con un servidor local

Durante el desarrollo, utiliza un servidor SMTP de pruebas o una herramienta que capture mensajes. Esto evita enviar correos reales por accidente. También puedes abstraer el transportador y sustituirlo por una función falsa en los tests.

Pruebas unitarias

def test_crear_mensaje():
    mensaje = crear_mensaje(
        "[email protected]",
        "[email protected]",
        "Prueba",
        "Contenido",
    )

    assert mensaje["Subject"] == "Prueba"
    assert "Contenido" in mensaje.get_body(preferencelist=("plain",)).get_content()

Prueba también adjuntos, caracteres Unicode, varios destinatarios y datos incompletos.

Errores frecuentes

Los problemas habituales son guardar contraseñas en el código, usar el puerto equivocado, olvidar STARTTLS, construir mensajes manualmente, cargar adjuntos enormes, revelar destinatarios y ejecutar reintentos sin límite. También es común usar la contraseña normal cuando el proveedor exige una contraseña de aplicación u OAuth.

Conclusión

La combinación de EmailMessage y smtplib permite automatizar correos fiables con la biblioteca estándar. Protege credenciales, utiliza TLS, incluye texto alternativo, valida adjuntos, controla reintentos y registra cada resultado. Para grandes volúmenes, delega la entrega en un proveedor especializado y mantén el consentimiento como requisito central.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Compactação de arquivos ZIP usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo descomprimir archivos ZIP con Python de forma segura

    Descomprime archivos ZIP con Python de forma segura: valida rutas, evita Zip Slip, limita tamaños, verifica integridad y procesa archivos

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Acesso e edição de Google Sheets com Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo acceder y editar Google Sheets con Python

    Accede y edita Google Sheets con Python y gspread: autenticación, lectura, escritura por lotes, validación, permisos, reintentos y seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Automação de mensagens no WhatsApp usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear un bot de WhatsApp con Python de forma responsable

    Crea un bot de WhatsApp con Python usando PyWhatKit o la Cloud API oficial, con validación, seguridad, colas, pruebas y

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Bot em Python para monitorar produtos e avisar quando houver queda de preço automaticamente
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear un bot de alerta de precios con Python

    Crea un bot de alerta de precios con Python, Requests y Beautiful Soup, usando Decimal, estado persistente, correo, logs y

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Extração de texto de arquivos PDF usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo extraer texto de archivos PDF con Python

    Extrae texto de PDF con Python usando pypdf y pdfplumber, procesa carpetas, tablas, archivos protegidos y detecta cuándo necesitas OCR.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Automação de tarefas com Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    20 scripts de Python para automatizar tareas cotidianas

    Descubre 20 scripts de Python para organizar archivos, crear backups, procesar CSV, enviar correos, comprobar sitios y automatizar tareas seguras.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026