本文爲譯文,原文連接 Sending Emails With Python
本人博客: 編程禪師html
你可能由於想使用Python發送電子郵件而找到了本教程。 也許你但願寫代碼來接收郵件提醒,在用戶建立賬戶時向用戶發送確認郵件,或向組織成員發送郵件以提醒他們支付會費。 發送郵件是一項耗時且容易出錯的任務,可是使用Python能夠輕鬆實現自動化。python
在本教程中,你將瞭解如何:git
使用 SMTP_SSL()
和 .starttls()
設置安全鏈接github
使用Python的內置 smtplib
庫發送基本電子郵件shell
使用 email
包發送包含HTML內容和附件的電子郵件編程
發送多份包含聯繫人數據的CSV文件的個性化電子郵件api
使用 Yagmail
包只需幾行代碼便可經過Gmail賬戶發送電子郵件安全
你將在本教程結束時找到一些事務性的電子郵件服務,當你想要發送大量電子郵件時,這些服務會頗有用。服務器
Python內置了 smtplib 模塊,用於使用簡單郵件傳輸協議(SMTP)發送電子郵件。 smtplib
在SMTP中使用 RFC 821協議。 本教程中的示例將使用Gmail SMTP服務器發送電子郵件,但相同的原則適用於其餘電子郵件服務。 雖然大多數電子郵件提供商使用與本教程中相同的鏈接端口,但你可使用Google搜索來快速確認。app
要開始本教程,請先創建用於開發的Gmail賬戶,或者創建一個SMTP調試服務器,郵件將不會發送而是將其打印到控制檯中。 下面列出了這兩個選項。 本地SMTP調試服務器可用於修復電子郵件功能的任何問題,並確保在發送任何電子郵件以前你的電子郵件功能沒有錯誤。
若是你決定使用Gmail賬戶發送電子郵件,我強烈建議你爲開發代碼設置一次性賬戶。 這是由於你必須調整Gmail賬戶的安全設置來容許從你的Python代碼訪問,並且你也可能會意外地泄露你的登陸詳細信息。 此外,我發現個人測試賬戶的收件箱很快就填滿了測試電子郵件,這也足以讓你設置一個新的Gmail賬戶進行開發。
Gmail的一個不錯的功能是,你可使用 +
號在@符號前面的電子郵件地址中添加任何修飾符。 例如,發送到 my+person1@gmail.com
和 my+person2@gmail.com
的郵件都將發送到 my@gmail.com
。 在測試電子郵件功能時,你可使用此功能模擬全部指向同一收件箱的多個地址。
要建立用於測試代碼的Gmail地址,請執行如下操做:
若是你不想下降Gmail賬戶的安全設置,請查看Google的文檔,瞭解如何使用Python腳本進行OAuth2受權來獲取訪問憑據。
你可使用Python預先安裝的 smptd
模塊運行一個本地SMTP調試服務器來測試電子郵件功能。 它不會將電子郵件發送到指定的地址,而是丟棄它們並將其內容打印到控制檯。 運行本地調試服務器意味着無需處理消息加密或使用憑據登陸電子郵件服務器。
你能夠經過在命令提示符下輸入如下內容來啓動本地SMTP調試服務器:
python -m smtpd -c DebuggingServer -n localhost:1025
複製代碼
在Linux上,在相同的命令以前加上 sudo
。
經過此服務器發送的任何電子郵件都將被丟棄,並在終端窗口中每行顯示一個 bytes
對象:
---------- MESSAGE FOLLOWS ----------
b'X-Peer: ::1'
b''
b'From: my@address.com'
b'To: your@address.com'
b'Subject: a local test mail'
b''
b'Hello there, here is a test email'
------------ END MESSAGE ------------
複製代碼
對於本教程的其他部分,我假設你使用的是Gmail賬戶,但若是你使用的是本地調試服務器,請確保使用 localhost
做爲SMTP服務器地址並使用端口爲1025而不是端口465或587。 除此以外,你不須要使用 login()
或使用 SSL / TLS
加密通訊。
在咱們深刻發送包含HTML內容和附件的電子郵件以前,你將學會使用Python發送純文本電子郵件。 這些是你能夠在簡單的文本編輯器中編寫的電子郵件。 沒有像文本格式或超連接這樣的奇特東西。 這些稍後你會了解到。
當你經過Python發送電子郵件時,應確保你的SMTP鏈接已加密,以便其餘人沒法輕鬆訪問你的郵件和登陸憑據。 SSL(安全套接字層)和TLS(傳輸層安全性)是兩種可用於加密SMTP鏈接的協議。 使用本地調試服務器時,沒必要使用其中任何一個。
有兩種方法能夠與你的電子郵件服務器創建安全鏈接:
SMTP_SSL()
創建安全的SMTP鏈接.starttls()
加密在這兩種狀況下,Gmail都會使用TLS加密電子郵件,由於這是SSL更安全的後續版本。 根據Python的安全注意事項,強烈建議你使用 ssl 模塊中的 create_default_context()
。 這將加載系統的可信CA證書,啓用主機名檢查和證書驗證,並嘗試選擇適合的安全的協議和密碼設置。
若是要檢查Gmail收件箱中電子郵件的加密信息,請轉到更多→顯示原始內容來查看「已接收」標題下列出的加密類型。
smtplib 是Python的內置模塊,用於使用SMTP或ESMTP監聽器守護程序向任何Internet計算機發送電子郵件。
我將首先向你展現如何使用 SMTP_SSL()
,由於它從一開始就實例化一個安全的鏈接,而且比使用 .starttls()
替代方案略微簡潔。 請注意,若是使用 SMTP_SSL()
,則Gmail要求你鏈接到465端口,使用 .starttls()
時,要求鏈接到589端口。
下面的代碼示例使用 smtplib
的 SMTP_SSL()
初始化TLS加密鏈接,以此來與Gmail的SMTP服務器創建安全鏈接。 ssl
的默認上下文驗證主機名及其證書,並優化鏈接的安全性。 請務必填寫你本身的電子郵件地址而不是 my@gmail.com
:
import smtplib, ssl
port = 465 # For SSL
password = input("Type your password and press enter: ")
# Create a secure SSL context
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
server.login("my@gmail.com", password)
# TODO: Send email here
複製代碼
使用 with smtplib.SMTP_SSL() as server
確保鏈接在縮進代碼塊的末尾自動關閉。 若是port爲零或未指定,則 .SMTP_SSL()
將使用標準端口(端口465)。
將電子郵件密碼存儲在代碼中並不安全,特別是若是你打算與他人共享。 相反,使用input()
讓用戶在運行腳本時輸入密碼,如上例所示。 若是你在輸入時不但願密碼顯示在屏幕上,則能夠導入 getpass 模塊並使用 .getpass()
代替直接輸入密碼。
咱們能夠建立一個不安全的SMTP鏈接並使用 .starttls()
加密它,而不是使用 .SMTP_SSL()
一開始就建立是安全的鏈接。
爲此,建立一個 smtplib.SMTP
實例,該實例封裝SMTP鏈接並容許你訪問其方法。 我建議你在腳本開頭定義SMTP服務器和端口,以便輕鬆配置它們。
下面的代碼片斷使用 server= SMTP()
,而不是使用 with SMTP() as server:
這個咱們在上一個示例中使用了的格式。 爲了確保在出現問題時代碼不會崩潰,請將主代碼放在 try
塊中,讓 except
塊將任何錯誤消息打印到 stdout
:
import smtplib, ssl
smtp_server = "smtp.gmail.com"
port = 587 # For starttls
sender_email = "my@gmail.com"
password = input("Type your password and press enter: ")
# Create a secure SSL context
context = ssl.create_default_context()
# Try to log in to server and send email
try:
server = smtplib.SMTP(smtp_server,port)
server.ehlo() # Can be omitted
server.starttls(context=context) # Secure the connection
server.ehlo() # Can be omitted
server.login(sender_email, password)
# TODO: Send email here
except Exception as e:
# Print any error messages to stdout
print(e)
finally:
server.quit()
複製代碼
要通知服務器讓它知道本身,應在建立 .SMTP()
對象後調用 .helo()
(SMTP)或 .ehlo()
(ESMTP),並在 .starttls()
後再調用。 此函數由 .starttls()
和 .sendmail()
隱式調用,所以除非你要檢查服務器的SMTP服務擴展,不然沒必要顯式調用 .helo()
或 .ehlo()
。
使用上述任一方法發起一個安全SMTP鏈接後,你可使用 .sendmail()
發送電子郵件:
server.sendmail(sender_mail, receiver_email, message)
複製代碼
我建議在 import
後在腳本頂部定義電子郵件地址和郵件內容,以便你能夠方便更改它們:
sender_email = "my@gmail.com"
receiver_email = "your@gmail.com"
message = """\ Subject: Hi there This message is sent from Python."""
# Send email here
複製代碼
message
字符串以 「Subject:Hi there」
開頭,後跟兩個換行符(\ n)。 這樣能夠確保 Hi there
爲電子郵件的主題顯示,而且新的一個行後面的文本將被視爲郵件正文。
下面的代碼示例使用 SMTP_SSL()
發送純文本電子郵件:
import smtplib, ssl
port = 465 # For SSL
smtp_server = "smtp.gmail.com"
sender_email = "my@gmail.com" # Enter your address
receiver_email = "your@gmail.com" # Enter receiver address
password = input("Type your password and press enter: ")
message = """\ Subject: Hi there This message is sent from Python."""
context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
複製代碼
這是經過使用 .starttls()
加密SMTP鏈接發送純文本電子郵件的一個代碼示例。做爲對照, server.ehlo()
行能夠省略,由於若是須要,它們將由 .starttls()
和 .sendmail()
隱式調用:
import smtplib, ssl
port = 587 # For starttls
smtp_server = "smtp.gmail.com"
sender_email = "my@gmail.com"
receiver_email = "your@gmail.com"
password = input("Type your password and press enter:")
message = """\ Subject: Hi there This message is sent from Python."""
context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, port) as server:
server.ehlo() # Can be omitted
server.starttls(context=context)
server.ehlo() # Can be omitted
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
複製代碼
Python的內置 email
包容許你構建更多花哨的電子郵件,而後可使用 smptlib
進行傳輸。 下面,你將瞭解如何使用 email` 包發送包含HTML內容和附件的電子郵件。
若是你要格式化電子郵件中的文本(粗體,斜體等),或者若是要添加任何圖像,超連接或響應式的內容,則使用HTML很是方便。 今天最多見的電子郵件類型是MIME(多用途因特網郵件擴展)多部分的電子郵件,它結合了HTML和純文本。 MIME消息由Python的 email.mime
模塊處理。 有關詳細說明,請查看文檔 。
因爲並不是全部電子郵件客戶端都默認顯示HTML內容,而且出於安全緣由,有些人僅選擇接收純文本電子郵件,所以爲HTML消息添加純文本的替代很是重要。 因爲電子郵件客戶端將首先渲染最後一部分的附件,所以請確保在純文本版本以後添加HTML消息。
在下面的示例中,咱們的 MIMEText()
對象將包含消息的HTML和純文本版本,MIMEMultipart("alternative")
實例將這些組合成一個帶有兩個備用渲染選項的消息:
import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
sender_email = "my@gmail.com"
receiver_email = "your@gmail.com"
password = input("Type your password and press enter:")
message = MIMEMultipart("alternative")
message["Subject"] = "multipart test"
message["From"] = sender_email
message["To"] = receiver_email
# Create the plain-text and HTML version of your message
text = """\ Hi, How are you? Real Python has many great tutorials: www.realpython.com"""
html = """\ <html> <body> <p>Hi,<br> How are you?<br> <a href="http://www.realpython.com">Real Python</a> has many great tutorials. </p> </body> </html> """
# Turn these into plain/html MIMEText objects
part1 = MIMEText(text, "plain")
part2 = MIMEText(html, "html")
# Add HTML/plain-text parts to MIMEMultipart message
# The email client will try to render the last part first
message.attach(part1)
message.attach(part2)
# Create secure connection with server and send email
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
server.login(sender_email, password)
server.sendmail(
sender_email, receiver_email, message.as_string()
)
複製代碼
在此示例中,首先將純文本和HTML消息定義爲字符串文字,而後將它們存儲爲 plain / html MIMEText
對象。 而後能夠按順序將這些添加到 MIMEMultipart("alternative")
消息中,並經過與電子郵件服務器的安全鏈接發送。 請記住在替代的純文本後添加HTML消息,由於電子郵件客戶端將嘗試首先渲染最後一個子部分。
爲了將二進制文件發送到設計用於處理文本數據的電子郵件服務器,須要在傳輸以前對其進行編碼。 這一般使用base64完成,它將二進制數據編碼爲可打印的ASCII字符。
下面的代碼示例展現瞭如何在發送電子郵件時將PDF文件做爲附件:
import email, smtplib, ssl
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
subject = "An email with attachment from Python"
body = "This is an email with attachment sent from Python"
sender_email = "my@gmail.com"
receiver_email = "your@gmail.com"
password = input("Type your password and press enter:")
# Create a multipart message and set headers
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject
message["Bcc"] = receiver_email # Recommended for mass emails
# Add body to email
message.attach(MIMEText(body, "plain"))
filename = "document.pdf" # In same directory as script
# Open PDF file in binary mode
with open(filename, "rb") as attachment:
# Add file as application/octet-stream
# Email client can usually download this automatically as attachment
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
# Encode file in ASCII characters to send by email
encoders.encode_base64(part)
# Add header as key/value pair to attachment part
part.add_header(
"Content-Disposition",
f"attachment; filename= {filename}",
)
# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()
# Log in to server using secure context and send email
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, text)
複製代碼
MIMEultipart()
接受RFC5233樣式的鍵/值對形式的參數,這些參數存儲在字典中並傳遞給 Message
基類的 .add_header
方法。
查看Python的 email.mime
模塊的文檔,瞭解有關使用MIME類的更多信息。
想象一下,你但願向你組織的成員發送電子郵件,以提醒他們支付他們的捐款費用。 或者,你可能但願向班級中的學生髮送個性化電子郵件,其中包含最近做業的成績。 這些任務在Python中垂手可得。
發送多封個性化電子郵件的簡單開始是建立包含全部必需我的信息的CSV(逗號分隔值)文件。 (確保在未經他們贊成的狀況下不共享其餘人的私人信息。)CSV文件能夠被視爲一個簡單的表,其中第一行一般包含列標題。
如下是 contacts_file.csv
文件的內容,我將其保存在與Python代碼相同的文件夾中。 它包含一組虛構人物的名稱,地址和成績。 我使用 my+modifier@gmail.com
構造來確保全部電子郵件最終都在我本身的收件箱中,在此示例中爲 my@gmail.com
:
name,email,grade
Ron Obvious,my+ovious@gmail.com,B+
Killer Rabbit of Caerbannog,my+rabbit@gmail.com,A
Brian Cohen,my+brian@gmail.com,C
複製代碼
建立CSV文件時,請確保使用逗號分隔你的值,而不包含任何的空格。
下面的代碼示例顯示瞭如何打開CSV文件並循環其內容行(跳過標題行)。 爲了確保代碼在你向全部聯繫人發送電子郵件以前正常運行。我已經爲每一個聯繫人打印了 Sending email to ...
,咱們稍後能夠用實際發送電子郵件的功能替換它們:
import csv
with open("contacts_file.csv") as file:
reader = csv.reader(file)
next(reader) # Skip header row
for name, email, grade in reader:
print(f"Sending email to {name}")
# Send email here
複製代碼
在上面的示例中,使用 open(filename) as file
: 確保你的文件在代碼塊的末尾關閉。 csv.reader()
能夠逐行讀取CSV文件並提取其值。 next(reader)
會跳過標題行,接下的一行 for name, email, grade in reader
:使用Python中的解包操做,並將結果值存儲在當前聯繫人的名稱,電子郵件和成績中。
若是CSV文件中的值包含任一側或兩側的空格,則可使用 .strip()
方法刪除它們。
你可使用 str.format()
填充大括號佔位符,將個性化內容放入消息中。 例如,"hi {name}, you {result} your assignment".format(name="John", result="passed")
會給你 "hi John, you passed your assignment"
。
從Python 3.6開始,使用f-string能夠更優雅地完成字符串格式化,但這些須要在f-string自己以前定義佔位符。 爲了在腳本開頭定義電子郵件消息,並在循環CSV文件時填寫每一個聯繫人的佔位符,使用較舊的 .format()
方法。
考慮到這一點,你能夠設置一個通用消息體,其中能夠爲我的定製佔位符。
如下代碼示例容許你向多個聯繫人發送個性化電子郵件。 它會循環CSV文件顯示每一個聯繫人的姓名,電子郵件,成績的,如上例所示。
常規消息在腳本開頭定義,對於CSV文件中的每一個聯繫人,其 {name}
和 {grade}
佔位符都已填入,而且經過與Gmail服務器的安全鏈接發送個性化電子郵件,正如你以前看過的:
import csv, smtplib, ssl
message = """Subject: Your grade Hi {name}, your grade is {grade}"""
from_address = "my@gmail.com"
password = input("Type your password and press enter: ")
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
server.login(from_address, password)
with open("contacts_file.csv") as file:
reader = csv.reader(file)
next(reader) # Skip header row
for name, email, grade in reader:
server.sendmail(
from_address,
email,
message.format(name=name,grade=grade),
)
複製代碼
有多個庫可讓您更輕鬆地發送電子郵件,例如 Envelopes,Flanker 和 Yagmail。 Yagmail旨在專門用於Gmail,它經過友好的API大大簡化了發送電子郵件的過程,以下面的代碼示例所示:
import yagmail
receiver = "your@gmail.com"
body = "Hello there from Yagmail"
filename = "document.pdf"
yag = yagmail.SMTP("my@gmail.com")
yag.send(
to=receiver,
subject="Yagmail test with attachment",
contents=body,
attachments=filename,
)
複製代碼
此代碼示例發送帶有PDF附件的電子郵件,比咱們使用 email
和 smtplib
發送郵件的例子代碼量大大減小 。
設置Yagmail時,你能夠將Gmail驗證添加到操做系統的密鑰環中,如文檔中所述。 若是你不這樣作,Yagmail會在須要時提示你輸入密碼並自動將其存儲在密鑰環中。
若是你計劃發送大量電子郵件,想要查看電子郵件統計信息,並但願確保可靠的投放,則可能須要查看事務性電子郵件服務。 雖然如下全部服務都發送大量電子郵件的付費套餐,但他們還提供免費套餐,以便你能夠試用它們。 其中一些免費計劃無限期有效,可能足以知足你的電子郵件需求。
如下是一些主要事務性電子郵件服務的免費計劃概述。 單擊提供商名稱將轉到其網站的訂價部分。
供應商 | 免費套餐 |
---|---|
Sendgrid | 起初30天內40000封免費,接下來100封/天 |
Sendinblue | 200 封/天 |
Mailgun | 開始的10000封免費 |
Mailjet | 200 封/天 |
Amazon SES | 62,000 封/月 |
你能夠運行Google搜索以查看最符合你需求的提供商,或者只是嘗試一些免費計劃,以查看你最喜歡哪一種API。
這是一個使用Sendgrid發送電子郵件的代碼示例,爲你提供如何使用Python的事務性電子郵件服務的方法:
import os
import sendgrid
from sendgrid.helpers.mail import Content, Email, Mail
sg = sendgrid.SendGridAPIClient(
apikey=os.environ.get("SENDGRID_API_KEY")
)
from_email = Email("my@gmail.com")
to_email = Email("your@gmail.com")
subject = "A test email from Sendgrid"
content = Content(
"text/plain", "Here's a test email sent through Python"
)
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())
# The statements below can be included for debugging purposes
print(response.status_code)
print(response.body)
print(response.headers)
複製代碼
要運行此代碼,你必須先:
setx SENDGRID_API_KEY 「YOUR_API_KEY」
(永久存儲此API密鑰)來添加API密鑰,或者設置 SENDGRID_API_KEY YOUR_API_KEY
以僅爲當前客戶端會話存儲它有關如何在Mac和Windows設置Sendgrid,能夠在Github上的存儲庫README中找到更多信息。
你如今能夠啓動安全的SMTP鏈接,並向聯繫人列表中的人員發送多個個性化電子郵件!
你已經學會了如何使用純文本替代方式發送HTML電子郵件,並將文件附加到你的電子郵件中。 當你使用Gmail賬戶時,Yagmail軟件包可簡化全部這些任務。 若是你計劃發送大量電子郵件,則值得研究事務性電子郵件服務。
享受用Python發送電子郵件,但記住:請不要垃圾郵件!
關注公衆號 <代碼與藝術>,學習更多國外精品技術文章。