Як надіслати електронну пошту декільком одержувачам за допомогою python smtplib?


198

Після довгих пошуків я не зміг дізнатися, як використовувати 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()

1
Здається, ОП відповів на власне запитання: sendmailпотрібен список.
Cees Timmerman


За допомогою Python3 мені довелося перебирати реципієнтів; for addr in recipients: msg['To'] = addrі тоді це спрацювало. Кілька присвоєнь фактично додають новий заголовок "Кому" до кожного. Це дуже химерний інтерфейс, я навіть не можу пояснити, як я думав спробувати його. Я навіть розглядав subprocessможливість зателефонувати на sendmailпакет Unix, щоб зберегти розум, перш ніж я зрозумів це.
mehtunguh

Відповіді:


312

Це дійсно працює , я витратив багато часу на те, щоб спробувати кілька варіантів.

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, і з цим фрагментом коду я тепер можу надіслати електронний лист.
strah_matrix

5
Це не буде надіслано декільком одержувачам, якщо ви використовуєте Python 3, який вам потрібен send_messageзамість sendmailкоментаря Антуана нижче та Python docs.python.org/3/library/email-examples.html
cardamom

Ви повинні використовувати для кожної траверси одержувачів для sendmail, інакше лише перший елемент отримає пошту.
Джонні

2
виправлення вказаної вище URL-адреси: docs.python.org/3/library/email.examples.html
Девід

151

msg['To']Повинна бути рядок:

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

У той час як recipientsв sendmail(sender, recipients, message)потреби бути список:

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

1
Це одне дивне дизайнерське рішення дляsmtplib.
Адама Матана

2
recipientsне повинен бути списком - якщо дано рядок, він розглядається як список з одним елементом. msg['To']Рядок може бути просто опущений.
Сузана

Я не дуже розумію, як "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>. Тому ваш приклад коду не рекомендується , оскільки він не зможе доставити цю пошту, оскільки лише розділивши її на ,вас, все ще не буде дійсних адрес!


2
RFC 2822 накладає максимальну ширину 988 символів для заданого заголовка і рекомендовану ширину 78 символів. Вам потрібно буде забезпечити "складання" заголовка, якщо у вас занадто багато адрес.
Стів Хант

Це має бути прийнятою відповіддю, оскільки це насправді пояснює, чому і як.
Серрано

Чудова відповідь. Що з полями електронної пошти CC та BCC? Я припускаю, що ми також повинні включати електронну пошту CC та BCC в smtp.send. І лише список CC (а не список BCC) у полях msg?
Тагар

Так, це працює. Поштові сервери, швидше за все, скинуть поле BCC (щоб запобігти цьому не було видно, і я не думаю, що це все роблять), але вони не розберуть його.
Є QUIT - Anonymous-Mousse

19

Це працює для мене.

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 повинен мати список: (відправник, >>> СПИСОК ТУТ <<<, msg.as_string ()). Це означає, як неприємно виглядає, що ви не можете використовувати один і той же тип [рядок або список] для обох полів
Паскаль Луї-Марі,

Працює як шарм для мене. Пітон 3.7.3.
Анадин

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())

Цілий простий код FYR нижче: 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

Тому насправді проблема полягає в тому, що для 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())

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()

7

Я зрозумів це кілька місяців тому і про це веду блог . Підсумок:

Якщо ви хочете використовувати smtplib для надсилання електронної пошти декільком одержувачам, використовуйте email.Message.add_header('To', eachRecipientAsString)для їх додавання, а потім, коли ви посилаєтесь на метод sendmail, use email.Message.get_all('To')надішліть повідомлення всім їм. Дітто для одержувачів Cc та Bcc.


Python 3.7 кидає виняток із повідомленням: сталося виняток: ValueError Може бути не більше 1 Для заголовків у повідомленні
Войцех Якубас

6

Рішення нижче працювало для мене. Він успішно надсилає електронне повідомлення декільком одержувачам, включаючи "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, ви повинні опустити рядок BCC bcc = ['mailid_5', 'mailid_6'], інакше це буде показано в заголовку, перемагаючи призначення bcc. Тестували на gmail та іншому поштовому сервері.
Віл

@Як ви б реалізували BCC у такому випадку?
3pitt

4

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

s.send_message(msg)

замість

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

4

Я використовую python 3.6, і наступний код працює для мене

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')

Він працював для мене лише з функцією send_message та використанням функції приєднання у списку з одержувачами, python 3.6.


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')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.