Làm thế nào để gửi email đến nhiều người nhận bằng python smtplib?


196

Sau khi tìm kiếm nhiều, tôi không thể tìm ra cách sử dụng smtplib.sendmail để gửi cho nhiều người nhận. Vấn đề là mỗi khi thư được gửi, các tiêu đề thư sẽ xuất hiện để chứa nhiều địa chỉ, nhưng thực tế chỉ có người nhận đầu tiên mới nhận được email.

Vấn đề dường như là email.Messagemô-đun mong đợi một cái gì đó khác với smtplib.sendmail()chức năng.

Nói tóm lại, để gửi cho nhiều người nhận, bạn nên đặt tiêu đề thành một chuỗi các địa chỉ email được phân tách bằng dấu phẩy. Các sendmail()tham số to_addrstuy nhiên phải là một danh sách các địa chỉ email.

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
Có vẻ như OP đã trả lời câu hỏi của chính mình: sendmailcần một danh sách.
Cees Timmerman


Sử dụng Python3 tôi đã phải lặp qua người nhận; for addr in recipients: msg['To'] = addrvà sau đó nó hoạt động. Nhiều bài tập thực sự nối thêm tiêu đề 'Tới' mới cho mỗi bài. Đây là một giao diện rất kỳ quái, tôi thậm chí không thể giải thích cách tôi nghĩ để thử nó. Tôi thậm chí đã cân nhắc sử dụng subprocessđể gọi sendmailgói unix để tiết kiệm sự tỉnh táo của mình trước khi tôi phát hiện ra điều này.
mehtunguh

Câu trả lời:


310

Điều này thực sự hiệu quả , tôi đã dành rất nhiều thời gian để thử nhiều biến thể.

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
tài liệu này có ví dụ:tolist =["one@one.org","two@two.org","three@three.org","four@four.org"]
chug2k

2
cảm ơn bạn @sorin cho kịch bản này. Tôi đã gặp sự cố khi gửi email từ tập lệnh python và với đoạn mã này, bây giờ tôi có thể gửi email.
sợ_matrix

5
Điều này sẽ không gửi cho nhiều người nhận nếu bạn đang sử dụng Python 3 mà bạn cần send_messagethay vì sendmailtheo nhận xét của Antoine bên dưới và tài liệu Python docs.python.org/3/l Library / email
cardamom

Bạn phải sử dụng cho mỗi lần truy cập mà người nhận cho sendmail, nếu không chỉ phần tử đầu tiên sẽ nhận được thư.
Johnny

2
sửa lỗi url được đề cập ở trên: docs.python.org/3/l Library / email.examples.html
David

150

Các msg['To']nhu cầu phải là một chuỗi:

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

Trong khi recipientstrong sendmail(sender, recipients, message)nhu cầu trở thành một danh sách:

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

1
Đây là một quyết định thiết kế kỳ lạ chosmtplib.
Adam Matan

2
recipientskhông phải là một danh sách - nếu một chuỗi được đưa ra, nó được coi là một danh sách với một phần tử. Các msg['To']chuỗi có thể dễ dàng được bỏ qua.
Suzana

Tôi thực sự không hiểu, làm thế nào 'a@a.com, b@b.com' được phân tích cú pháp để chỉ địa chỉ đầu tiên nhận được email. Nhưng cảm ơn! Đây là câu trả lời, đã phải đưa danh sách vào đó.
anton lượn sóng

đã làm việc cho tôi và nó phù hợp với tài liệu trong docs.python.org/2/l Library / email
Rodrigo Laguna

43

Bạn cần hiểu sự khác biệt giữa địa chỉ hiển thị của email và việc gửi .

msg["To"]về cơ bản là những gì được in trên thư. Nó không thực sự có tác dụng. Ngoại trừ việc khách hàng email của bạn, giống như nhân viên bưu điện thông thường, sẽ cho rằng đây là người bạn muốn gửi email đến.

Tuy nhiên, giao hàng thực tế có thể làm việc khá khác nhau. Vì vậy, bạn có thể thả email (hoặc một bản sao) vào hộp thư của một người hoàn toàn khác.

Có nhiều lý do khác nhau cho việc này. Ví dụ chuyển tiếp . Trường To:tiêu đề không thay đổi khi chuyển tiếp, tuy nhiên email được thả vào một hộp thư khác.

Các smtp.sendmaillệnh bây giờ sẽ chăm sóc của các thực tế giao hàng. email.Messagelà nội dung của thư duy nhất, không phải là giao hàng.

Ở cấp độ thấp SMTP, bạn cần cung cấp từng cái một cho người nhận, đó là lý do tại sao một danh sách các địa chỉ (không bao gồm tên!) Là API hợp lý.

Đối với tiêu đề, nó cũng có thể chứa ví dụ như tên, ví dụ To: First Last <email@addr.tld>, Other User <other@mail.tld>. Do đó, ví dụ mã của bạn không được khuyến nghị , vì nó sẽ thất bại trong việc gửi thư này, vì chỉ bằng cách chia nó trên ,bạn vẫn không có địa chỉ hợp lệ!


2
RFC 2822 áp đặt chiều rộng tối đa 988 ký tự cho một tiêu đề nhất định và chiều rộng được đề xuất là 78 ​​ký tự. Bạn sẽ cần đảm bảo bạn "gấp" tiêu đề nếu bạn có quá nhiều địa chỉ.
Steve Hunt

Đây phải là câu trả lời được chấp nhận, vì nó thực sự giải thích lý do tại sao và như thế nào.
Serrano

Câu trả lời chính xác. Còn các trường email CC và BCC thì sao? Tôi giả sử chúng ta cũng phải bao gồm email CC và BCC trong smtp.send. Và chỉ có danh sách CC (chứ không phải danh sách BCC) trong các trường tin nhắn?
Tagar

Vâng, đó là cách nó hoạt động. Các máy chủ thư có thể sẽ bỏ trường BCC (để ngăn điều này hiển thị và tôi không nghĩ tất cả đều như vậy), nhưng họ sẽ không phân tích cú pháp.
Có QUIT - Anony-Mousse

19

Nó làm việc cho tôi.

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

bạn đang sử dụng phiên bản nào của python? Tôi gặp vấn đề tương tự như poster ban đầu và tôi đang sử dụng python 2.7.9
panofish

1
Tại sao không chỉ đơn giản là recipients = ['john.doe@example.com','john.smith@example.co.uk']thay vì làm cho nó một chuỗi, và sau đó tách nó để tạo một danh sách?
WoJ

thành Woj, vì tin nhắn ['To'] phải là một chuỗi và s.sendmail nên có một danh sách: (người gửi, >>> DANH SÁCH TẠI ĐÂY <<<, dir.as_opes ()). Điều đó có nghĩa là, khó chịu như vẻ ngoài của nó, rằng bạn không thể sử dụng cùng một loại [chuỗi hoặc danh sách] cho cả hai trường
Pascal Louis-Marie

Hoạt động như một cơ duyên đối với tôi. Python 3.7.3.
Anadyn

11

Tôi đã thử dưới đây và nó hoạt động như một bùa mê :)

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

Mã hoàn toàn đơn giản của FYR bên dưới: 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

Vì vậy, thực sự vấn đề là SMTP.sendmail và email.MIMEText cần hai thứ khác nhau.

email.MIMEText thiết lập tiêu đề "Tới:" cho phần thân của email. Nó CHỈ được sử dụng để hiển thị kết quả cho con người ở đầu kia và giống như tất cả các tiêu đề e-mail, phải là một chuỗi đơn. (Lưu ý rằng thực sự không cần phải làm gì với những người thực sự nhận được tin nhắn.)

Mặt khác, SMTP.sendmail sẽ thiết lập "phong bì" của tin nhắn cho giao thức SMTP. Nó cần một danh sách các chuỗi Python, mỗi chuỗi có một địa chỉ duy nhất.

Vì vậy, những gì bạn cần làm là COMBINE hai câu trả lời bạn nhận được. Đặt thông điệp ['To'] thành một chuỗi, nhưng chuyển danh sách thô thành sendmail:

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

7

Tôi đã đưa ra chức năng mô-đun nhập khẩu này. Nó sử dụng máy chủ email gmail trong ví dụ này. Nó được chia thành tiêu đề và thông điệp để bạn có thể thấy rõ những gì đang diễn ra:

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

Tôi đã tìm ra điều này một vài tháng trước và viết blog về nó . Tóm tắt là:

Nếu bạn muốn sử dụng smtplib để gửi email đến nhiều người nhận, hãy sử dụng email.Message.add_header('To', eachRecipientAsString)để thêm họ và sau đó khi bạn gọi phương thức sendmail, hãy use email.Message.get_all('To')gửi tin nhắn cho tất cả họ. Ditto cho người nhận Cc và Bcc.


Python 3.7 đưa ra một ngoại lệ với thông báo: Ngoại lệ đã xảy ra: ValueError Có thể có nhiều nhất 1 tiêu đề trong một tin nhắn
Wojciech Jakubas

6

Các giải pháp dưới đây làm việc cho tôi. Nó gửi thành công một email đến nhiều người nhận, bao gồm "CC" và "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
Điều này hoạt động một phần, để thực sự ẩn BCC, bạn phải bỏ qua dòng BCC bcc = ['mailid_5', 'mailid_6'] nếu không điều này sẽ hiển thị trong tiêu đề đánh bại mục đích của bcc. Đã thử nghiệm với gmail và máy chủ thư khác.
Wil

@ Làm thế nào bạn sẽ thực hiện BCC trong trường hợp đó?
3pitt

4

Chà, phương pháp trong phương pháp asnwer này không hiệu quả với tôi. Tôi không biết, có thể đây là Python3 (tôi đang sử dụng phiên bản 3.4) hoặc sự cố liên quan đến gmail, nhưng sau một số lần thử, giải pháp hiệu quả với tôi, là dòng

s.send_message(msg)

thay vì

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

4

Tôi sử dụng python 3.6 và đoạn mã sau hoạt động với tôi

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

Nó chỉ hoạt động với tôi với hàm send_message và sử dụng hàm nối trong danh sách người nhận, python 3.6.


0

bạn có thể thử điều này khi bạn viết email người nhận trên một tệp văn bản

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')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.