您可能发现本教程是因为您想使用 Python 发送电子邮件。也许您希望从您的代码接收电子邮件提醒,在用户创建帐户时向他们发送确认电子邮件,或者向您组织的成员发送电子邮件以提醒他们支付会费。手动发送电子邮件是一项耗时且容易出错的任务,但使用 Python 可以轻松实现自动化。
在本教程中,您将学习如何:
-
设置一个安全连接使用SMTP_SSL()
和.starttls()
-
使用Python的内置smtplib
库发送基本电子邮件
-
发送电子邮件HTML 内容和附件使用email
包裹
-
发送多个个性化电子邮件使用包含联系人数据的 CSV 文件
-
使用雅格邮件仅使用几行代码即可通过 Gmail 帐户发送电子邮件的包
在本教程的最后,您将找到一些事务性电子邮件服务,当您想要发送大量电子邮件时,这些服务将非常有用。
免费下载: 从 Python Tricks: The Book 中获取示例章节它通过简单的示例向您展示了 Python 的最佳实践,您可以立即应用这些示例来编写更漂亮的 Python 代码。
入门
Python带有内置的smtplib使用简单邮件传输协议 (SMTP) 发送电子邮件的模块。smtplib
使用RFC 821SMTP 协议。本教程中的示例将使用 Gmail SMTP 服务器发送电子邮件,但相同的原则也适用于其他电子邮件服务。尽管大多数电子邮件提供商使用与本教程中的连接端口相同的连接端口,但您可以快速运行谷歌搜索来确认你的。
要开始学习本教程,设置 Gmail 帐户以进行开发, 或者设置 SMTP 调试服务器它会丢弃您发送的电子邮件并将其打印到命令提示符。下面为您列出了这两个选项。本地 SMTP 调试服务器可用于修复电子邮件功能的任何问题,并在发送任何电子邮件之前确保您的电子邮件功能没有错误。
选项 1:设置 Gmail 帐户进行开发
如果您决定使用 Gmail 帐户发送电子邮件,我强烈建议您设置一个一次性帐户来开发代码。这是因为您必须调整 Gmail 帐户的安全设置以允许从 Python 代码进行访问,并且您有可能意外泄露您的登录详细信息。另外,我发现我的测试帐户的收件箱很快就充满了测试电子邮件,这足以让我建立一个新的 Gmail 帐户进行开发。
Gmail 的一个很好的功能是您可以使用+
在您的电子邮件地址之前添加任何修饰符@
符号。例如,邮件发送至my+person1@gmail.com
和my+person2@gmail.com
都将到达my@gmail.com
。测试电子邮件功能时,您可以使用它来模拟全部指向同一收件箱的多个地址。
要设置 Gmail 地址来测试您的代码,请执行以下操作:
-
创建一个新的 Google 帐户.
- 转动允许安全性较低的应用程序开启。请注意,这可以让其他人更轻松地访问您的帐户。
如果您不想降低 Gmail 帐户的安全设置,请查看 Google 的文档了解如何使用 OAuth2 授权框架获取 Python 脚本的访问凭据。
选项 2:设置本地 SMTP 服务器
您可以通过运行本地 SMTP 调试服务器来测试电子邮件功能,使用smtpd
Python 中预装的模块。它不会将电子邮件发送到指定的地址,而是会丢弃它们并将其内容打印到控制台。运行本地调试服务器意味着无需处理消息加密或使用凭据登录电子邮件服务器。
您可以通过在命令提示符中键入以下内容来启动本地 SMTP 调试服务器:
$ python -m smtpd -c DebuggingServer -n localhost:1025
在 Linux 上,使用前面带有相同命令sudo
.
通过此服务器发送的任何电子邮件都将被丢弃并在终端窗口中显示为字节每行对象:
---------- 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 发送纯文本电子邮件。您可以在简单的文本编辑器中编写这些电子邮件。没有文本格式或超链接之类的花哨的东西。稍后你就会了解到这一点。
启动安全 SMTP 连接
当您通过 Python 发送电子邮件时,应确保您的 SMTP 连接已加密,以便其他人不易访问您的消息和登录凭据。 SSL(安全套接字层)和 TLS(传输层安全性)是可用于加密 SMTP 连接的两种协议。使用本地调试服务器时没有必要使用其中任何一个。
有两种方法可以启动与电子邮件服务器的安全连接:
- 使用以下命令启动从一开始就受到保护的 SMTP 连接
SMTP_SSL()
.
- 启动不安全的 SMTP 连接,然后可以使用该连接进行加密
.starttls()
.
在这两种情况下,Gmail 都会使用 TLS 加密电子邮件,因为这是 SSL 更安全的后继者。根据Python的安全考虑,强烈建议您使用create_default_context()
来自ssl模块。这将加载系统的可信 CA 证书,启用主机名检查和证书验证,并尝试选择相当安全的协议和密码设置。
如果您想检查 Gmail 收件箱中电子邮件的加密情况,请转至更多的 → 显示原件查看下面列出的加密类型已收到标头。
smtplib是 Python 的内置模块,用于通过 SMTP 或 ESMTP 侦听器守护进程将电子邮件发送到任何互联网计算机。
我将向您展示如何使用SMTP_SSL()
首先,因为它实例化了一个从一开始就安全的连接,并且比.starttls()
选择。请记住,如果使用 Gmail,则需要连接到端口 465SMTP_SSL()
,并在使用时连接到端口 587.starttls()
.
选项 1:使用SMTP_SSL()
下面的代码示例使用 Gmail 的 SMTP 服务器创建安全连接SMTP_SSL()
的smtplib
启动 TLS 加密连接。默认上下文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()
将使用基于 SSL 的 SMTP 标准端口(端口 465)。
将电子邮件密码存储在代码中并不是安全的做法,尤其是当您打算与其他人共享时。相反,使用input()
让用户在运行脚本时输入密码,如上例所示。如果您不希望在键入密码时在屏幕上显示密码,您可以导入通行证模块及使用.getpass()
而不是盲目输入密码。
选项 2:使用.starttls()
而不是使用.SMTP_SSL()
要创建从一开始就安全的连接,我们可以创建不安全的 SMTP 连接并使用.starttls()
.
为此,请创建一个实例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()
为了向服务器表明您的身份,.helo()
(SMTP) 或.ehlo()
(ESMTP) 应在创建后调用.SMTP()
对象,然后再次.starttls()
。该函数被隐式调用.starttls()
和.sendmail()
如果需要的话,所以除非你想检查服务器的SMTP服务扩展,否则没有必要使用.helo()
或者.ehlo()
明确地。
发送您的纯文本电子邮件
使用上述任一方法启动安全 SMTP 连接后,您可以使用.sendmail()
,它几乎按照罐头上的说明进行操作:
server.sendmail(sender_email, receiver_email, message)
我建议在导入后在脚本顶部定义电子邮件地址和消息内容,以便您可以轻松更改它们:
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)
为了进行比较,下面是一个代码示例,它通过受保护的 SMTP 连接发送纯文本电子邮件.starttls()
。这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
包允许您构造更多精美的电子邮件,然后可以使用smtplib
正如您已经所做的那样。下面,您将了解如何使用email
用于发送包含 HTML 内容和附件的电子邮件的包。
包括 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 消息,因为电子邮件客户端将尝试首先呈现最后一个子部分。
使用添加附件email
包裹
为了将二进制文件发送到设计用于处理文本数据的电子邮件服务器,需要在传输之前对其进行编码。这最常使用64位基数,它将二进制数据编码为可打印的 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-style键/值对,存储在字典中并传递给.add_header方法的信息基类。
查看文档对于 Python 来说email.mime
模块以了解有关使用 MIME 类的更多信息。
发送多封个性化电子邮件
想象一下,您想向组织成员发送电子邮件,提醒他们支付贡献费。或者,您可能想向班级中的学生发送个性化电子邮件,其中包含他们最近作业的成绩。这些任务在 Python 中轻而易举。
制作包含相关个人信息的 CSV 文件
发送多封个性化电子邮件的一个简单起点是创建 CSV(逗号分隔值)文件其中包含所有必需的个人信息。 (确保不要在未经他人同意的情况下分享他人的私人信息。)CSV 文件可以被视为一个简单的表格,其中第一行通常包含列标题。
以下是文件的内容contacts_file.csv
,我将其保存在与 Python 代码相同的文件夹中。它包含一组虚构人物的姓名、地址和等级。我用了my+modifier@gmail.com
确保所有电子邮件最终都进入我自己的收件箱的结构,在本例中是我的@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
在上面的例子中,使用with open(filename) as file:
确保您的文件在代码块末尾关闭。csv.reader()
可以轻松地逐行读取 CSV 文件并提取其值。这next(reader)
行跳过标题行,以便下面的行for name, email, grade in reader:
在每个逗号处分割后续行,并将结果值存储在字符串中name
, email
和grade
对于当前联系人。
如果 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 弦,但这些要求在 f 字符串本身之前定义占位符。为了在脚本开头定义电子邮件消息,并在循环 CSV 文件时为每个联系人填写占位符,旧版本.format()
使用方法。
考虑到这一点,您可以设置一个通用消息正文,其中包含可以针对个人定制的占位符。
代码示例
以下代码示例可让您向多个联系人发送个性化电子邮件。它循环使用 CSV 文件name,email,grade
对于每个联系人,如上面的例子.
一般消息在脚本的开头定义,对于 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),
)
雅格邮件
有多个库旨在使发送电子邮件变得更容易,例如信封, 侧卫和雅格邮件。 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附件在我们所需的一小部分行中使用电子邮件和 smtplib 的示例.
设置 Yagmail 时,您可以将 Gmail 验证添加到操作系统的密钥环中,如中所述文档。如果您不这样做,Yagmail 将在需要时提示您输入密码并将其自动存储在密钥环中。
交易电子邮件服务
如果您计划发送大量电子邮件、想要查看电子邮件统计信息并希望确保可靠的发送,那么可能值得考虑交易电子邮件服务。
尽管以下所有服务都有用于发送大量电子邮件的付费计划,但它们也附带免费计划,因此您可以尝试一下。其中一些免费计划无限期有效,可能足以满足您的电子邮件需求。
以下是一些主要交易电子邮件服务的免费计划的概述。单击提供商名称将带您进入其网站的定价部分。
Provider |
Free plan |
Sendgrid |
40,000 emails for your first 30 days, then 100/day |
Sendinblue |
300 emails/day |
Mailgun |
First 10,000 emails free |
Mailjet |
200 emails/day |
Amazon SES |
62,000 emails/month |
你可以运行一个谷歌搜索查看哪个提供商最适合您的需求,或者尝试一些免费计划来查看您最喜欢使用哪个 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)
要运行此代码,您必须首先:
-
注册(免费)Sendgrid 帐户
-
请求 API 密钥用于用户验证
- 通过键入添加您的 API 密钥
setx SENDGRID_API_KEY "YOUR_API_KEY"
在命令提示符中(永久存储此 API 密钥)或set SENDGRID_API_KEY YOUR_API_KEY
仅为当前客户端会话存储它
有关如何为 Mac 和 Windows 设置 Sendgrid 的更多信息,请参阅存储库的自述文件:吉图布.
结论
您现在可以启动安全的 SMTP 连接并向联系人列表中的人员发送多封个性化电子邮件!
您已经了解了如何使用纯文本替代方案发送 HTML 电子邮件以及如何将文件附加到电子邮件中。这雅格邮件当您使用 Gmail 帐户时,该软件包可以简化所有这些任务。如果您计划发送大量电子邮件,那么值得考虑交易电子邮件服务。
享受使用 Python 发送电子邮件的乐趣,并记住:请不要发送垃圾邮件!