Как отправить электронное письмо нескольким получателям с помощью python smtplib?

215

После долгих поисков я не мог понять, как использовать smtplib.sendmail для отправки нескольким получателям. Проблема заключалась в том, что каждый раз, когда отправлялось письмо, заголовки писем содержали несколько адресов, но на самом деле только первый получатель получал письмо.

Проблема, похоже, в том, что email.Messageмодуль ожидает чего-то отличного от smtplib.sendmail()функции.

Короче говоря, для отправки нескольким получателям вы должны установить заголовок в виде строки адресов электронной почты, разделенных запятыми. Однако sendmail()параметр to_addrsдолжен быть списком адресов электронной почты.

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()
user1148320
источник
1
Похоже, ОП ответил на свой вопрос: sendmailнужен список.
Cees Timmerman
1
Используя Python3, мне пришлось перебирать получателей; for addr in recipients: msg['To'] = addrи тогда это сработало. Несколько назначений фактически добавляют новый заголовок «Кому» для каждого из них. Это очень причудливый интерфейс, даже не могу объяснить, как я задумал его попробовать. Я даже подумывал использовать subprocessвызов sendmailпакета unix , чтобы сохранить рассудок, прежде чем понял это.
mehtunguh 07

Ответы:

326

Это действительно работает , я потратил много времени, пробуя несколько вариантов.

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
в документации есть пример:tolist =["one@one.org","two@two.org","three@three.org","four@four.org"]
chug2k
2
спасибо @sorin за этот скрипт. У меня возникла проблема с отправкой электронного письма из скрипта Python, и с помощью этого фрагмента кода я теперь могу отправить электронное письмо.
fear_matrix
8
Это не будет отправлено нескольким получателям, если вы используете Python 3, который вам нужен, send_messageа не sendmailв соответствии с комментарием Антуана ниже и документами Python docs.python.org/3/library/email-examples.html
cardamom
1
Вы должны использовать для каждого обхода, что получатели для sendmail, иначе только первый элемент получит почту.
Джонни
2
исправление к указанному выше URL: docs.python.org/3/library/email.examples.html
Дэвид
155

Это msg['To']должна быть строка:

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

Хотя recipientsin sendmail(sender, recipients, message)должен быть список:

sendmail("a@a.com", ["a@b.com", "b@b.com", "c@b.com"], "Howdy")
dvdhns
источник
1
Это одно странное дизайнерское решение дляsmtplib.
Adam Matan
2
recipientsне обязательно должен быть списком - если дана строка, она рассматривается как список с одним элементом. msg['To']Строка может быть просто опущен.
Suzana
1
Я действительно не понимаю, как анализируется 'a@a.com, b@b.com', поэтому только первый адрес получает электронное письмо. Но спасибо! Это ответ, нужно было поместить туда список.
antonavy
работал у меня, и это согласуется с документацией в docs.python.org/2/library/email-examples.html
Родриго Лагуна,
44

Вы должны понимать разницу между видимым адресом электронной почты и доставкой .

msg["To"]по сути то, что напечатано на письме. На самом деле это не имеет никакого эффекта. За исключением того, что ваш почтовый клиент, как и обычный почтовый служащий, будет считать, что это то, кому вы хотите отправить электронное письмо.

Однако фактическая доставка может быть совершенно иной. Таким образом, вы можете бросить электронное письмо (или копию) в почтовый ящик совершенно другого человека.

На то есть разные причины. Например пересылка . Поле To:заголовка не меняется при пересылке, однако электронное письмо попадает в другой почтовый ящик.

Теперь smtp.sendmailкоманда позаботится о фактической доставке. email.Messageэто только содержание письма, а не доставка.

На низком уровне SMTPвам нужно сообщать получателям по одному, поэтому список адресов (не включая имена!) - это разумный API.

Для заголовка он также может содержать, например, имя, например To: First Last <email@addr.tld>, Other User <other@mail.tld>. Таким образом, ваш пример кода не рекомендуется , так как он не сможет доставить это письмо, поскольку, просто разделив его, у ,вас все еще нет действительных адресов!

ВЫЙТИ - Anony-Mousse
источник
2
RFC 2822 устанавливает максимальную ширину 988 символов для данного заголовка и рекомендованную ширину 78 символов. Вам нужно будет убедиться, что вы «свернули» заголовок, если у вас слишком много адресов.
Стив Хант,
Это должен быть принятый ответ, поскольку он фактически объясняет, почему и как.
Серрано
Отличный ответ. Как насчет полей электронной почты CC и BCC? Я предполагаю, что мы также должны включить электронную почту CC и BCC в smtp.send. И только список CC (а не список BCC) в полях сообщения?
Тагар
Да, вот как это работает. Почтовые серверы, скорее всего, сбросят поле BCC (чтобы оно не было видимым, а я не думаю, что все они это делают), но они не будут его анализировать.
ВЫЙТИ - Anony-Mousse
22

Меня устраивает.

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())
классный парень
источник
какую версию Python вы используете? У меня та же проблема, что и на исходном плакате, и я использую python 2.7.9
panofish
1
Почему бы просто recipients = ['john.doe@example.com','john.smith@example.co.uk']вместо того, чтобы превратить его в строку, а затем разбить ее, чтобы составить список?
WoJ
в Woj, потому что msg ['To'] должно быть строкой, а s.sendmail должен иметь список: (sender, >>> LIST HERE <<<, msg.as_string ()). Это означает, как бы раздражает это ни выглядело, что вы не можете использовать один и тот же тип [строка или список] для обоих полей
Паскаль Луи-Мари,
Для меня работает как оберег. Python 3.7.3.
Anadyn
Спасибо, у меня работает в Databricks и python 3.7.3
anand
13

На самом деле проблема в том, что для SMTP.sendmail и email.MIMEText нужны две разные вещи.

email.MIMEText устанавливает заголовок «Кому:» для тела письма. Он используется ТОЛЬКО для отображения результата человеку на другом конце и, как и все заголовки электронной почты, должен быть одной строкой. (Обратите внимание, что на самом деле это не имеет ничего общего с людьми, которые действительно получают сообщение.)

SMTP.sendmail, с другой стороны, устанавливает «конверт» сообщения для протокола SMTP. Ему нужен список строк Python, каждая из которых имеет единственный адрес.

Итак, что вам нужно сделать, это ОБЪЕДИНИТЬ два полученных ответа. Установите msg ['To'] в одну строку, но передайте необработанный список в sendmail:

emails = ['a.com','b.com', 'c.com']
msg['To'] = ', '.join( emails ) 
....
s.sendmail( msg['From'], emails, msg.as_string())
СайСандип Голла
источник
11

Я попробовал следующее, и это сработало как шарм :)

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())
MasterMind
источник
Полный простой код 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()
FYR
9

Приведенное ниже решение сработало для меня. Он успешно отправляет электронное письмо нескольким получателям, включая «CC» и «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
Это частично работает, чтобы действительно скрыть скрытую копию, вы должны опустить строку скрытой копии bcc = ['mailid_5', 'mailid_6'], иначе это будет отображаться в заголовке, что противоречит назначению скрытой копии. Протестировано с gmail и другим почтовым сервером.
Wil
@Wil, как бы вы реализовали BCC в этом случае?
3pitt
7

Я придумал эту функцию импортируемого модуля. В этом примере он использует почтовый сервер Gmail. Он разделен на заголовок и сообщение, чтобы вы могли четко видеть, что происходит:

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()
Radtek
источник
7

Я понял это несколько месяцев назад и написал об этом в блоге . Резюме:

Если вы хотите использовать smtplib для отправки электронной почты нескольким получателям, используйте email.Message.add_header('To', eachRecipientAsString)для их добавления, а затем, когда вы вызываете метод sendmail, use email.Message.get_all('To')отправьте сообщение всем из них. То же самое для получателей Cc и Bcc.

Джеймс Макферсон
источник
Python 3.7 выдает исключение с сообщением: Exception has been: ValueError В сообщении может быть не более 1 заголовка To
Войцех Якубас
6
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')

У меня это сработало только с функцией send_message и с использованием функции соединения в списке с получателями, python 3.6.

Гильерме Энрике Мендес
источник
5

Я использую python 3.6, и у меня работает следующий код

email_send = 'xxxxx@xxx.xxx,xxxx@xxx.xxx'
server.sendmail(email_user,email_send.split(','),text)    
Роби
источник
4

Что ж, метод в этом методе asnwer у меня не работал. Я не знаю, возможно, это проблема, связанная с Python3 (я использую версию 3.4) или gmail, но после некоторых попыток решение, которое сработало для меня, была строка

s.send_message(msg)

вместо того

s.sendmail(sender, recipients, msg.as_string())
Антуан
источник
0

вы можете попробовать это, когда пишете электронные письма получателя в текстовый файл

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')
Skiller Dz
источник