Como enviar email para vários destinatários usando python smtplib?

196

Depois de muita pesquisa, não consegui descobrir como usar o smtplib.sendmail para enviar para vários destinatários. O problema era que, sempre que o email era enviado, os cabeçalhos pareciam conter vários endereços, mas na verdade apenas o primeiro destinatário recebia o email.

O problema parece ser que o email.Messagemódulo espera algo diferente da smtplib.sendmail()função.

Em resumo, para enviar para vários destinatários, você deve definir o cabeçalho como uma sequência de endereços de email delimitados por vírgula. O sendmail()parâmetro, to_addrsno entanto, deve ser uma lista de endereços de email.

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()
user1148320
fonte
1
Parece que OP respondeu à sua própria pergunta: sendmailprecisa de uma lista.
Cees Timmerman
Usando o Python3, tive que percorrer os destinatários; for addr in recipients: msg['To'] = addre então funcionou. Na verdade, várias atribuições acrescentam um novo cabeçalho 'Para' a cada uma. Essa é uma interface muito bizarra, eu nem consigo explicar como pensei em experimentar. Eu estava pensando em usar subprocesso sendmailpacote unix para salvar minha sanidade antes de descobrir isso.
mehtunguh

Respostas:

311

Isso realmente funciona , passei muito tempo tentando várias 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
fonte
3
a documentação tem o exemplo:tolist =["[email protected]","[email protected]","[email protected]","[email protected]"]
chug2k
2
obrigado @sorin por este script. Eu estava com problemas para enviar um email de um script python e, com esse pedaço de código, agora posso enviar o email.
Fear_matrix 14/07/2015
5
Isso não será enviado a vários destinatários se você estiver usando o Python 3, em send_messagevez de usar sendmailo comentário de Antoine abaixo e os documentos do Python docs.python.org/3/library/email-examples.html
cardamom
Você deve usar para cada passagem que os destinatários do sendmail, caso contrário, apenas o primeiro elemento receberá o email.
Johnny
2
correção para o URL mencionado acima: docs.python.org/3/library/email.examples.html
David
150

A msg['To']precisa ser uma string:

msg['To'] = "[email protected], [email protected], [email protected]"

Enquanto o recipientsin sendmail(sender, recipients, message)precisa ser uma lista:

sendmail("[email protected]", ["[email protected]", "[email protected]", "[email protected]"], "Howdy")
dvdhns
fonte
1
Esta é uma decisão de design estranha parasmtplib.
Adam Matan
2
recipientsnão precisa ser uma lista - se uma string for fornecida, ela será tratada como uma lista com um elemento. A msg['To']string pode ser simplesmente omitida.
Suzana
Realmente não entendo como '[email protected], [email protected]' é analisado para que apenas o primeiro endereço receba o email. Mas obrigado! Esta é a resposta, teve que colocar a lista lá.
antonavy
trabalhou para mim, e é consistente com a documentação em docs.python.org/2/library/email-examples.html
Rodrigo Laguna
43

Você precisa entender a diferença entre o endereço visível de um email e a entrega .

msg["To"]é essencialmente o que está impresso na carta. Na verdade, não tem nenhum efeito. Exceto que o seu cliente de email, assim como o oficial de correios, presumirá que é para quem você deseja enviar o email.

A entrega real, no entanto, pode funcionar bem diferente. Então você pode soltar o email (ou uma cópia) na caixa postal de alguém completamente diferente.

Há várias razões para isto. Por exemplo encaminhamento . oTo: campo do cabeçalho não muda no encaminhamento, no entanto, o email é descartado em uma caixa de correio diferente.

O smtp.sendmailcomando agora cuida da entrega real .email.Messageé apenas o conteúdo da carta, não a entrega.

Em nível baixo SMTP, você precisa fornecer aos destinatários um por um, e é por isso que uma lista de endereços (sem incluir nomes!) É a API sensata.

Para o cabeçalho, ele também pode conter, por exemplo, o nome, por exemplo To: First Last <[email protected]>, Other User <[email protected]>. Portanto, seu exemplo de código não é recomendado , pois ele falhará ao entregar este email, pois, ao dividi-lo, ,você ainda não possui os endereços válidos!

Possui QUIT - Anony-Mousse
fonte
2
O RFC 2822 impõe uma largura máxima de 988 caracteres para um determinado cabeçalho e uma largura recomendada de 78 caracteres. Você precisará garantir que "dobre" o cabeçalho se você tiver muitos endereços.
Steve caça
Essa deve ser a resposta aceita, pois na verdade explica o porquê e como.
Serrano
Ótima resposta. E os campos de email CC e BCC? Suponho que também tenhamos que incluir os emails CC e BCC no smtp.send. E apenas a lista CC (e não a lista BCC) nos campos msg?
Tagar
Sim, é assim que funciona. Os servidores de email provavelmente eliminarão o campo Cco (para impedir que isso fique visível, e acho que não), mas eles não serão analisados.
Saiu - Anony-Mousse
19

Funciona para mim.

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())
cara legal
fonte
qual versão do python você está usando? Recebo o mesmo problema que o poster original e estou usando python 2.7.9
panofish
1
Por que não simplesmente em recipients = ['[email protected]','[email protected]']vez de torná-lo uma string e depois dividi-lo para fazer uma lista?
WoJ 16/04/19
para o Woj, porque msg ['To'] deve ser uma string e s.sendmail deve ter uma lista: (remetente, >>> LIST AQUI <<<, msg.as_string ()). Isso significa que, por mais irritante que pareça, você não pode usar um mesmo tipo [string ou lista] para os dois campos
Pascal Louis-Marie
Funciona como um encanto para mim. Python 3.7.3.
Anadyn
11

Eu tentei o abaixo e funcionou como um encanto :)

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())
MasterMind
fonte
FYR todo código simples abaixo: 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

Na verdade, o problema é que SMTP.sendmail e email.MIMEText precisam de duas coisas diferentes.

email.MIMEText configura o cabeçalho "Para:" para o corpo do email. É usado SOMENTE para exibir um resultado ao ser humano na outra extremidade e, como todos os cabeçalhos de email, deve ser uma única sequência. (Observe que ele realmente não tem nada a ver com as pessoas que realmente recebem a mensagem.)

SMTP.sendmail, por outro lado, configura o "envelope" da mensagem para o protocolo SMTP. Ele precisa de uma lista de strings Python, cada uma com um único endereço.

Então, o que você precisa fazer é COMBINAR as duas respostas que você recebeu. Defina msg ['To'] como uma única string, mas passe a lista bruta para sendmail:

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

Eu vim com esta função de módulo importável. Ele usa o servidor de email do gmail neste exemplo. É dividido em cabeçalho e mensagem para que você possa ver claramente o que está acontecendo:

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
fonte
7

Eu descobri isso alguns meses atrás e escrevi sobre isso . O resumo é:

Se você deseja usar o smtplib para enviar email para vários destinatários, use-os email.Message.add_header('To', eachRecipientAsString)para adicioná-los e, quando você chamar o método sendmail, use email.Message.get_all('To')envie a mensagem para todos eles. O mesmo vale para os destinatários Cc e Cco.

James McPherson
fonte
Python 3.7 lança uma exceção com a mensagem: Ocorreu uma exceção : ValueError Pode haver no máximo 1 Para cabeçalhos em uma mensagem
Wojciech Jakubas
6

A solução abaixo funcionou para mim. Ele envia com êxito um email para vários destinatários, incluindo "CC" e "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
fonte
1
Isso funciona parcialmente, para ocultar realmente o Cco, você deve omitir a linha Cco bcc = ['mailid_5', 'mailid_6']; caso contrário, isso será exibido no cabeçalho, derrotando o objetivo do cco. Testado com o Gmail e outro servidor de email.
10249 Wil as
@ Como você implementaria o BCC nesse caso?
3pitt 18/09/19
4

Bem, o método neste método asnwer não funcionou para mim. Não sei, talvez esse seja um problema relacionado ao Python3 (estou usando a versão 3.4) ou ao gmail, mas depois de algumas tentativas, a solução que funcionou para mim foi a linha

s.send_message(msg)

ao invés de

s.sendmail(sender, recipients, msg.as_string())
Antoine
fonte
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]')

Ele só funcionou para mim com a função send_message e usando a função join na lista de destinatários, python 3.6.

Guilherme Henrique Mendes
fonte
0

você pode tentar isso ao escrever os e-mails dos destinatários em um arquivo 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
fonte