¿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"] = "[email protected]"
msg["To"] = "[email protected],[email protected],[email protected]"
msg["Cc"] = "[email protected],[email protected]"
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()
usuario1148320
fuente
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 = '[email protected]'
recipients = ['[email protected]', '[email protected]']
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = ", ".join(recipients)
s.sendmail(sender, recipients, msg.as_string())
Sorin
fuente
3
la documentación tiene el ejemplo:tolist =["[email protected]","[email protected]","[email protected]","[email protected]"]
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'] = "[email protected], [email protected], [email protected]"

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

sendmail("[email protected]", ["[email protected]", "[email protected]", "[email protected]"], "Howdy")
dvdhns
fuente
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 '[email protected], [email protected]' 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 protected]>, Other User <[email protected]>. 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.

HA SALIDO - Anony-Mousse
fuente
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 = '[email protected]'
recipients = '[email protected],[email protected]'
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = recipients
s.sendmail(sender, recipients.split(','), msg.as_string())
coolguy
fuente
¿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 = ['[email protected]','[email protected]']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 =  ['[email protected]', '[email protected]']
rec =  ', '.join(rec_list)

msg['To'] = rec

send_out = smtplib.SMTP('localhost')
send_out.sendmail(me, rec_list, msg.as_string())
Cerebro
fuente
FYR código simple a continuación: import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText sender = '[email protected]' rec_list = ['[email protected]', '[email protected]'] 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())
SaiSandeep Golla
fuente
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 protected]', 'email2@another_email.com', '[email protected]']
    gmail_user = '[email protected]'
    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()
radtek
fuente
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.

James McPherson
fuente
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)
Omkar
fuente
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())
Antoine
fuente
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'] = '[email protected]'
    msg['To'] = (', ').join(recipients.split(','))

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

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

if __name__ == '__main__':
    sender('[email protected],[email protected]')

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.

Guilherme Henrique Mendes
fuente
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 = "[email protected]"
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')
Skiller Dz
fuente