¿Cómo enviar correos electrónicos a múltiples destinatarios usando python smtplib?


196

Después de mucho buscar, no pude descubrir cómo usar smtplib.sendmail para enviar a múltiples destinatarios. El problema era que cada vez que se enviaba el correo los encabezados de correo parecían contener varias direcciones, pero de hecho solo el primer destinatario recibiría el correo electrónico.

El problema parece ser que el email.Messagemódulo espera algo diferente a la smtplib.sendmail()función.

En resumen, para enviar a múltiples destinatarios, debe configurar el encabezado para que sea una cadena de direcciones de correo electrónico delimitadas por comas. Sin embargo, el sendmail()parámetro to_addrsdebe ser una lista de direcciones de correo electrónico.

from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
import smtplib

msg = MIMEMultipart()
msg["Subject"] = "Example"
msg["From"] = "me@example.com"
msg["To"] = "malcom@example.com,reynolds@example.com,firefly@example.com"
msg["Cc"] = "serenity@example.com,inara@example.com"
body = MIMEText("example email body")
msg.attach(body)
smtp = smtplib.SMTP("mailhost.example.com", 25)
smtp.sendmail(msg["From"], msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())
smtp.quit()

1
Parece que OP respondió a su propia pregunta: sendmailnecesita una lista.
Cees Timmerman


Usando Python3 tuve que recorrer los destinatarios; for addr in recipients: msg['To'] = addrY luego funcionó. Las asignaciones múltiples en realidad añaden un nuevo encabezado 'Para' para cada una. Esta es una interfaz muy extraña, ni siquiera puedo explicar cómo pensé en probarla. Incluso estaba considerando usar subprocesspara llamar al sendmailpaquete de Unix para salvar mi cordura antes de resolver esto.
Mehtunguh

Respuestas:


311

Esto realmente funciona , pasé mucho tiempo probando múltiples variantes.

import smtplib
from email.mime.text import MIMEText

s = smtplib.SMTP('smtp.uk.xensource.com')
s.set_debuglevel(1)
msg = MIMEText("""body""")
sender = 'me@example.com'
recipients = ['john.doe@example.com', 'john.smith@example.co.uk']
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = ", ".join(recipients)
s.sendmail(sender, recipients, msg.as_string())

3
la documentación tiene el ejemplo:tolist =["one@one.org","two@two.org","three@three.org","four@four.org"]
chug2k

2
gracias @sorin por este script. Estaba teniendo un problema para enviar un correo electrónico desde un script de Python y con este código, ahora puedo enviar el correo electrónico.
fear_matrix

55
Esto no se enviará a varios destinatarios si está utilizando Python 3 que necesita en send_messagelugar de sendmailsegún el comentario de Antoine a continuación y los documentos de Python docs.python.org/3/library/email-examples.html
cardamomo

Debe utilizar para cada recorrido que los destinatarios de sendmail, de lo contrario, solo el primer elemento recibirá el correo.
Johnny

2
corrección a la url mencionada anteriormente: docs.python.org/3/library/email.examples.html
David

150

La msg['To']necesidad de ser una cadena:

msg['To'] = "a@b.com, b@b.com, c@b.com"

Mientras que el recipientsen sendmail(sender, recipients, message)debe ser una lista:

sendmail("a@a.com", ["a@b.com", "b@b.com", "c@b.com"], "Howdy")

1
Esta es una decisión de diseño extraño parasmtplib.
Adam Matan

2
recipientsno tiene que ser una lista: si se proporciona una cadena, se trata como una lista con un elemento. La msg['To']cadena simplemente se puede omitir.
Suzana

Realmente no entiendo cómo se analiza 'a@a.com, b@b.com' para que solo la primera dirección reciba el correo electrónico. ¡Pero gracias! Esta es la respuesta, tenía que poner la lista allí.
antonavy

funcionó para mí, y es consistente con la documentación en docs.python.org/2/library/email-examples.html
Rodrigo Laguna

43

Debe comprender la diferencia entre la dirección visible de un correo electrónico y la entrega .

msg["To"]es esencialmente lo que está impreso en la carta. En realidad no tiene ningún efecto. Excepto que su cliente de correo electrónico, al igual que el oficial de correos regular, asumirá que es a quien desea enviar el correo electrónico.

Sin embargo, la entrega real puede funcionar bastante diferente. Por lo tanto, puede colocar el correo electrónico (o una copia) en la casilla de correo de alguien completamente diferente.

Hay varias razones para esto. Por ejemplo reenvío . El To:campo del encabezado no cambia al reenviar, sin embargo, el correo electrónico se coloca en un buzón diferente.

El smtp.sendmailcomando ahora se encarga de la entrega real . email.Messagees el contenido de la carta solamente, no la entrega.

En el nivel bajo SMTP, debe proporcionar los destinatarios uno por uno, por lo que una lista de direcciones (¡sin incluir nombres!) Es la API sensata.

Para el encabezado, también puede contener, por ejemplo, el nombre, por ejemplo To: First Last <email@addr.tld>, Other User <other@mail.tld>. Por lo tanto, no se recomienda su ejemplo de código , ya que fallará en la entrega de este correo, ya que solo dividiéndolo ,aún no tiene las direcciones válidas.


2
RFC 2822 impone un ancho máximo de 988 caracteres para un encabezado dado y un ancho recomendado de 78 caracteres. Tendrá que asegurarse de "doblar" el encabezado si tiene demasiadas direcciones.
Steve Hunt

Esta debería ser la respuesta aceptada, ya que en realidad explica el por qué y el cómo.
Serrano

Gran respuesta. ¿Qué pasa con los campos de correo electrónico CC y BCC? Supongo que también tenemos que incluir el correo electrónico CC y BCC en smtp.send. ¿Y solo la lista CC (y no la lista BCC) en los campos de msg?
Tagar

Sí, así es como funciona. Los servidores de correo probablemente eliminarán el campo BCC (para evitar que esto sea visible, y no creo que todos lo hagan), pero no lo analizarán.
HA SALIDO - Anony-Mousse

19

Esto funciona para mi.

import smtplib
from email.mime.text import MIMEText

s = smtplib.SMTP('smtp.uk.xensource.com')
s.set_debuglevel(1)
msg = MIMEText("""body""")
sender = 'me@example.com'
recipients = 'john.doe@example.com,john.smith@example.co.uk'
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = recipients
s.sendmail(sender, recipients.split(','), msg.as_string())

¿Qué versión de Python estás usando? Tengo el mismo problema que el póster original y estoy usando Python 2.7.9
panofish

1
¿Por qué no simplemente en recipients = ['john.doe@example.com','john.smith@example.co.uk']lugar de convertirlo en una cadena y luego dividirlo para hacer una lista?
WoJ

a Woj, porque msg ['To'] debería ser una cadena y s.sendmail debería tener una lista: (remitente, >>> LISTA AQUÍ <<<, msg.as_string ()). Eso significa, por molesto que parezca, que no puede usar un mismo tipo [cadena o lista] para ambos campos
Pascal Louis-Marie

Funciona como un encanto para mí. Python 3.7.3.
Anadyn

11

Intenté lo siguiente y funcionó a las mil maravillas :)

rec_list =  ['first@example.com', 'second@example.com']
rec =  ', '.join(rec_list)

msg['To'] = rec

send_out = smtplib.SMTP('localhost')
send_out.sendmail(me, rec_list, msg.as_string())

FYR código simple a continuación: import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText sender = 'myEmailAddress@example.com' rec_list = ['first@example.com', 'second@example.com'] rec = ', '.join(rec_list) msg = MIMEMultipart('alternative') msg['Subject'] = 'The required subject' msg['From'] = sender msg['To'] = rec html = ('whatever html code') htm_part = MIMEText(html, 'html') msg.attach(htm_part) send_out = smtplib.SMTP('localhost') send_out.sendmail(sender, rec_list, msg.as_string()) send_out.quit()
MasterMind

10

Entonces, en realidad, el problema es que SMTP.sendmail y email.MIMEText necesitan dos cosas diferentes.

email.MIMEText configura el encabezado "Para:" para el cuerpo del correo electrónico. SOLO se usa para mostrar un resultado al ser humano en el otro extremo y, como todos los encabezados de correo electrónico, debe ser una sola cadena. (Tenga en cuenta que en realidad no tiene nada que ver con las personas que realmente reciben el mensaje).

SMTP.sendmail, por otro lado, configura el "sobre" del mensaje para el protocolo SMTP. Necesita una lista de cadenas de Python, cada una de las cuales tiene una sola dirección.

Entonces, lo que debe hacer es COMBINAR las dos respuestas que recibió. Establezca msg ['Para'] en una sola cadena, pero pase la lista sin formato a sendmail:

emails = ['a.com','b.com', 'c.com']
msg['To'] = ', '.join( emails ) 
....
s.sendmail( msg['From'], emails, msg.as_string())

7

Se me ocurrió esta función de módulo importable. Utiliza el servidor de correo electrónico de gmail en este ejemplo. Se divide en encabezado y mensaje para que pueda ver claramente lo que está sucediendo:

import smtplib

def send_alert(subject=""):

    to = ['email@one.com', 'email2@another_email.com', 'a3rd@email.com']
    gmail_user = 'me@gmail.com'
    gmail_pwd = 'my_pass'
    smtpserver = smtplib.SMTP("smtp.gmail.com", 587)
    smtpserver.ehlo()
    smtpserver.starttls()
    smtpserver.ehlo
    smtpserver.login(gmail_user, gmail_pwd)
    header = 'To:' + ", ".join(to) + '\n' + 'From: ' + gmail_user + '\n' + 'Subject: ' + subject + '\n'
    msg = header + '\n' + subject + '\n\n'
    smtpserver.sendmail(gmail_user, to, msg)
    smtpserver.close()

7

Me di cuenta de esto hace unos meses y escribí en un blog al respecto . El resumen es:

Si desea usar smtplib para enviar correos electrónicos a múltiples destinatarios, use email.Message.add_header('To', eachRecipientAsString)para agregarlos y luego, cuando invoque el método sendmail, use email.Message.get_all('To')envíe el mensaje a todos ellos. Lo mismo para los destinatarios CC y CCO.


Python 3.7 arroja una excepción con el mensaje: Se ha producido una excepción : ValueError Puede haber como máximo 1 Para encabezados en un mensaje
Wojciech Jakubas

6

La solución a continuación funcionó para mí. Envía con éxito un correo electrónico a múltiples destinatarios, incluidos "CC" y "BCC".

toaddr = ['mailid_1','mailid_2']
cc = ['mailid_3','mailid_4']
bcc = ['mailid_5','mailid_6']
subject = 'Email from Python Code'
fromaddr = 'sender_mailid'
message = "\n  !! Hello... !!"

msg['From'] = fromaddr
msg['To'] = ', '.join(toaddr)
msg['Cc'] = ', '.join(cc)
msg['Bcc'] = ', '.join(bcc)
msg['Subject'] = subject

s.sendmail(fromaddr, (toaddr+cc+bcc) , message)

1
Esto funciona en parte, para ocultar realmente el BCC, debe omitir la línea BCC bcc = ['mailid_5', 'mailid_6'], de lo contrario, esto se mostrará en el encabezado en contra del propósito de bcc. Probado con gmail y otro servidor de correo.
Wil

@Wil, ¿cómo implementaría BCC en ese caso?
3pitt

4

Bueno, el método en este método no funcionó para mí. No sé, tal vez este es un Python3 (estoy usando la versión 3.4) o un problema relacionado con gmail, pero después de algunos intentos, la solución que funcionó para mí fue la línea

s.send_message(msg)

en vez de

s.sendmail(sender, recipients, msg.as_string())

4

Yo uso Python 3.6 y el siguiente código me funciona

email_send = 'xxxxx@xxx.xxx,xxxx@xxx.xxx'
server.sendmail(email_user,email_send.split(','),text)    

3
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def sender(recipients): 

    body = 'Your email content here'
    msg = MIMEMultipart()

    msg['Subject'] = 'Email Subject'
    msg['From'] = 'your.email@gmail.com'
    msg['To'] = (', ').join(recipients.split(','))

    msg.attach(MIMEText(body,'plain'))

    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    server.login('your.email@gmail.com', 'yourpassword')
    server.send_message(msg)
    server.quit()

if __name__ == '__main__':
    sender('email_1@domain.com,email_2@domain.com')

Solo funcionó para mí con la función send_message y usando la función de unión en la lista con los destinatarios, python 3.6.


0

puedes probar esto cuando escribes los correos electrónicos de destinatarios en un archivo de texto

from email.mime.text import MIMEText
from email.header import Header
import smtplib

f =  open('emails.txt', 'r').readlines()
for n in f:
     emails = n.rstrip()
server = smtplib.SMTP('smtp.uk.xensource.com')
server.ehlo()
server.starttls()
body = "Test Email"
subject = "Test"
from = "me@example.com"
to = emails
msg = MIMEText(body,'plain','utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] =  Header(from, 'utf-8')
msg['To'] = Header(to, 'utf-8')
text = msg.as_string()
try:
   server.send(from, emails, text)
   print('Message Sent Succesfully')
except:
   print('There Was An Error While Sending The Message')
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.