Jak wysłać wiadomość e-mail do wielu odbiorców za pomocą python smtplib?


197

Po wielu poszukiwaniach nie mogłem dowiedzieć się, jak używać smtplib.sendmail do wysyłania wiadomości do wielu odbiorców. Problem polegał na tym, że za każdym razem, gdy wiadomość była wysyłana, wydawało się, że nagłówki zawierają wiele adresów, ale tak naprawdę tylko pierwszy adresat otrzymał wiadomość.

Problem polega na tym, że email.Messagemoduł oczekuje czegoś innego niż smtplib.sendmail()funkcja.

Krótko mówiąc, aby wysłać do wielu odbiorców, należy ustawić nagłówek jako ciąg adresów e-mail rozdzielanych przecinkami. sendmail()Parametr to_addrsjednak powinna być lista adresów e-mail.

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
Wygląda na to, że OP odpowiedział na swoje własne pytanie: sendmailpotrzebuje listy.
Cees Timmerman,


Używając Python3 musiałem przeglądać odbiorców; for addr in recipients: msg['To'] = addri wtedy zadziałało. Wiele przypisań faktycznie dodaje nowy nagłówek „Do” dla każdego. To bardzo dziwny interfejs, nie umiem nawet wyjaśnić, jak go wypróbowałem. Zastanawiałem się nawet, czy nie subprocesszadzwonić do sendmailpakietu unix , aby uratować moje zdrowie psychiczne, zanim się zorientowałem.
mehtunguh

Odpowiedzi:


312

To naprawdę działa , spędziłem dużo czasu próbując wielu wariantów.

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
dokumentacja ma przykład:tolist =["one@one.org","two@two.org","three@three.org","four@four.org"]
chug2k

2
dziękuję @sorin za ten skrypt. Miałem problem z wysłaniem wiadomości e-mail ze skryptu Pythona, a dzięki temu fragmentowi kodu mogę teraz wysłać wiadomość e-mail.
fear_matrix

5
To nie zostanie wysłane do wielu odbiorców, jeśli używasz Pythona 3, którego potrzebujesz, send_messagezamiast sendmailzgodnie z poniższym komentarzem Antoine i dokumentacją Python docs.python.org/3/library/email-examples.html
cardamom

Musisz użyć dla każdego trawersu, który odbiorcy wysyłają do sendmaila, w przeciwnym razie tylko pierwszy element odbierze pocztę.
Johnny,

2
korekta adresu
David

150

msg['To']Musi być ciągiem:

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

Podczas gdy recipientsin sendmail(sender, recipients, message)musi być listą:

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

1
To jedna dziwna decyzja dotycząca projektusmtplib.
Adam Matan,

2
recipientsnie musi być listą - jeśli podany zostanie ciąg, jest traktowany jak lista z jednym elementem. msg['To']Ciąg można po prostu pominąć.
Suzana

Naprawdę nie rozumiem, w jaki sposób „a@a.com, b@b.com” jest analizowane, więc tylko pierwszy adres otrzymuje wiadomość e-mail. Ale dzięki! To jest odpowiedź, musiałam tam umieścić listę.
antonavy

pracował dla mnie i jest zgodny z dokumentacją w docs.python.org/2/library/email-examples.html
Rodrigo Laguna

44

Musisz zrozumieć różnicę między widocznym adresem e-mail a dostawą .

msg["To"]jest zasadniczo tym, co wydrukowano na liście. To nie ma żadnego efektu. Tyle że twój klient poczty, podobnie jak zwykły urzędnik pocztowy, przyjmie, że właśnie do tego chcesz wysłać wiadomość e-mail.

Rzeczywista dostawa może jednak działać zupełnie inaczej. Więc może upuścić e-mail (lub kopię) do postu polu kogoś zupełnie innego.

Są tego różne przyczyny. Na przykład przekazywanie . Pole To:nagłówka nie zmienia się podczas przekazywania, jednak wiadomość e-mail jest upuszczana w innej skrzynce pocztowej.

smtp.sendmailKomenda teraz dba o rzeczywistej dostawy. email.Messageto tylko treść listu, a nie dostawa.

Na niskim poziomie SMTPmusisz przekazywać odbiorcom jeden po drugim, dlatego lista adresów (bez nazw!) Jest rozsądnym API.

W nagłówku może również zawierać na przykład nazwę, np To: First Last <email@addr.tld>, Other User <other@mail.tld>. Dlatego twój przykładowy kod nie jest zalecany , ponieważ nie powiedzie się dostarczenie tej wiadomości, ponieważ po prostu dzieląc ją na ,ciebie, nadal nie masz prawidłowych adresów!


2
RFC 2822 narzuca maksymalną szerokość 988 znaków dla danego nagłówka i zalecaną szerokość 78 znaków. Jeśli masz zbyt wiele adresów, musisz „zagiąć” nagłówek.
Steve Hunt

To powinna być zaakceptowana odpowiedź, ponieważ faktycznie wyjaśnia, dlaczego i jak.
Serrano,

Świetna odpowiedź. Co z polami e-mail CC i BCC? Zakładam, że musimy również dołączyć e-maile CC i BCC do smtp.send. I tylko lista CC (a nie lista BCC) w polach msg?
Tagar

Tak, tak to działa. Serwery pocztowe prawdopodobnie upuszczą pole BCC (aby zapobiec temu, aby były widoczne, i nie sądzę, że wszyscy to robią), ale nie przeanalizują go.
Ma ZAKOŃCZENIE - Anony-Mousse

19

Mi to pasuje.

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

jakiej wersji Pythona używasz? Mam ten sam problem, co oryginalny plakat i używam Pythona 2.7.9
panofish,

1
Dlaczego nie po prostu recipients = ['john.doe@example.com','john.smith@example.co.uk']zamiast zrobić ciąg, a następnie podzielić go, aby utworzyć listę?
WoJ

do Woj, ponieważ msg ['To'] powinien być ciągiem, a s.sendmail powinien mieć listę: (nadawca, >>> LISTA TUTAJ <<<, msg.as_string ()). Oznacza to, że irytujące, jak się wydaje, że nie można użyć tego samego typu [ciągu lub listy] dla obu pól
Pascal Louis-Marie

Dla mnie działa jak urok. Python 3.7.3.
Anadyn

11

Próbowałem poniżej i zadziałało to jak urok :)

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

Pełny prosty kod 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

10

Problem polega na tym, że SMTP.sendmail i email.MIMEText potrzebują dwóch różnych rzeczy.

email.MIMEText ustawia nagłówek „Do:” dla treści wiadomości e-mail. Służy TYLKO do wyświetlania wyniku człowiekowi na drugim końcu i, podobnie jak wszystkie nagłówki wiadomości e-mail, musi być pojedynczym ciągiem. (Pamiętaj, że tak naprawdę nie musi to mieć nic wspólnego z osobami, które faktycznie otrzymują wiadomość).

Z drugiej strony SMTP.sendmail ustawia „kopertę” wiadomości dla protokołu SMTP. Potrzebuje listy napisów w języku Python, z których każdy ma jeden adres.

Musisz więc ŁĄCZYĆ dwie otrzymane odpowiedzi. Ustaw msg ['To'] na pojedynczy ciąg, ale przekaż nieprzetworzoną listę do sendmaila:

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

7

Wymyśliłem tę funkcję modułu do importowania. W tym przykładzie używa serwera e-mail Gmail. Podzielony jest na nagłówek i wiadomość, dzięki czemu możesz wyraźnie zobaczyć, co się dzieje:

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

Zrozumiałem to kilka miesięcy temu i napisałem o tym na blogu . Podsumowanie to:

Jeśli chcesz używać smtplib do wysyłania wiadomości e-mail do wielu odbiorców, użyj email.Message.add_header('To', eachRecipientAsString)ich, aby je dodać, a następnie, gdy wywołasz metodę sendmail, use email.Message.get_all('To')wyślij wiadomość do wszystkich. To samo dotyczy odbiorców DW i UDW.


Python 3.7 zgłasza wyjątek z komunikatem: Wystąpił wyjątek: ValueError Może być maksymalnie 1 Do nagłówków w wiadomości
Wojciech Jakubas,

6

Poniższe rozwiązanie działało dla mnie. Z powodzeniem wysyła wiadomość e-mail do wielu odbiorców, w tym „CC” i „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
Działa to częściowo, aby naprawdę ukryć BCC, musisz pominąć linię BCC bcc = ['mailid_5', 'mailid_6'], w przeciwnym razie wyświetli się w nagłówku pokonującym cel bcc. Testowane z Gmailem i innym serwerem poczty.
Wil

@ W jaki sposób wdrożyłbyś BCC w takim przypadku?
3pitt

4

Cóż, metoda w tej metodzie asnwer nie działała dla mnie. Nie wiem, może to jest problem związany z Python3 (używam wersji 3.4) lub Gmailem, ale po kilku próbach rozwiązaniem, które działało dla mnie, było linijka

s.send_message(msg)

zamiast

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

4

Używam Pythona 3.6, a następujący kod działa dla mnie

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

Działa tylko dla mnie z funkcją send_message i używaniem funkcji dołączania na liście z adresatami, python 3.6.


0

możesz tego spróbować, pisząc wiadomości e-mail o adresatach w pliku tekstowym

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')
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.