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"] = "[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()
użytkownik1148320
źródło
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 = '[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
źródło
3
dokumentacja ma przykład:tolist =["[email protected]","[email protected]","[email protected]","[email protected]"]
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'] = "[email protected], [email protected], [email protected]"

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

sendmail("[email protected]", ["[email protected]", "[email protected]", "[email protected]"], "Howdy")
dvdhns
źródło
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 „[email protected], [email protected]” 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 protected]>, Other User <[email protected]>. 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!

Ma ZAKOŃCZENIE - Anony-Mus
źródło
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 = '[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())
Spoko gość
źródło
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 = ['[email protected]','[email protected]']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 =  ['[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())
Mistrz umysłu
źródło
Pełny prosty kod 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()
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())
SaiSandeep Golla
źródło
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 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
źródło
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.

James McPherson
źródło
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)
Omkar
źródło
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())
Antoine
źródło
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]')

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

Guilherme Henrique Mendes
źródło
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 = "[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
źródło