Python 使用 SMTP 发送邮件
SMTP 全称为 Simple Mail Transfer Protocol,即简单邮件传输协议,它是一组用于从源地址到目的地址传送邮件的规则,同时会控制信件的中转方式,一般我们发送邮件都是通过这一协议来完成的。
Python 内置的 smtplib 模块对 SMTP 协议进行了简单的封装,借助它我们可以很轻松的实现用代码来发送邮件。
连接 SMTP 服务器
要发送邮件,很明显需要先连接到一个可用的邮件服务器,为此我们需要指定服务器地址和端口。
由于各种历史遗留问题,现在仍在使用的 SMTP 服务端口有三个,分别是:25端口(明文传输)、465端口(SSL 加密)和 587端口(STARTTLS 加密)。不同的端口处理情况稍有不同,下面在代码中分别演示三种端口的连接方式。
import smtplib
# 25端口(明文传输)
smtp_server = smtplib.SMTP(host="smtp.xxx.xxx", port=25)
# 465端口(SSL加密)
smtp_server = smtplib.SMTP_SSL(host="smtp.xxx.xxx", port=465)
# 587端口(STARTTLS加密)
smtp_server = smtplib.SMTP(host="smtp.xxx.xxx", port=587)
smtp_server.starttls()
登录 SMTP 服务器
连上服务器之后还需要用我们的邮箱登录才能发送邮件(注意 QQ 邮箱、163 邮箱等使用 SMTP 服务需要的密码是在后台申请的授权码,不是你在网页上登录邮箱时用的密码)。
smtp_server.login(user="test@xxx.xxx", password="test_password")
构造邮件
电子邮件本质上可以看作一种按特定格式组织的文本文件,除了正文内容之外,标准邮件一般还需要三个头部信息: From(发件人), To(收件人)和 Subject(邮件主题)。所以说发送邮件并不是将你想发的内容传过去就行了,我们还需要先按照一定的规则“构造”一封邮件。
常见的邮件主要有纯文本邮件、HTML 邮件、带附件的邮件几种类型,下面分别演示这三种类型邮件的构造方式。
纯文本邮件
from email.mime.text import MIMEText
content = "这是一封纯文本邮件"
subject = "纯文本邮件测试"
from_user = "test@xxx.xxx"
to_user = "test2@xxx.xxx"
# 构造邮件主体
my_mail = MIMEText(content, _subtype="plain", _charset="utf8")
# 添加发件人
my_mail["From"] = from_user
# 添加收件人
my_mail["To"] = to_user
# 添加邮件主题
my_mail["subject"] = subject
HTML 邮件
构造 HTML 邮件只需要将构造纯文本邮件代码中 MIMEText()
的 _subtype 参数修改为 html 即可,如下:
my_mail = MIMEText(content, _subtype="html", _charset="utf8")
带附件的邮件
带附件的邮件与上面两种稍有不同,我们需要借助 MIMEMultipart()
构造一封多组件邮件,再将文本内容、附件内容依次添加进去,代码如下:
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
content = "这是一封带附件的邮件"
subject = "带附件的邮件测试"
from_user = "test@xxx.xxx"
to_user = "test2@xxx.xxx"
file_path = "xxx/test.txt" # 附件所在的路径
file_name = "test.txt" # 附件在邮件中显示的文件名
file_content = open(file_path, "rb").read() # 读取附件内容
# 构造一封多组件的邮件
my_mail = MIMEMultipart()
# 往多组件邮件中加入文本内容
text_msg = MIMEText(content, _subtype="plain", _charset="utf8")
my_mail.attach(text_msg)
# 往多组件邮件中加入附件
file_msg = MIMEApplication(file_content)
file_msg.add_header("content-disposition", "attachment", filename=file_name)
my_mail.attach(file_msg)
# 添加发件人
my_mail["From"] = from_user
# 添加收件人
my_mail["To"] = to_user
# 添加邮件主题
my_mail["subject"] = subject
发送邮件
构造好了邮件,连接并登录了 SMTP 服务器,接下来要发送邮件就很简单了,直接调用 send_message()
函数即可。
# 连接到服务器并登录好的SMTP对象
smtp_server = ......
# 前面构造好的邮件
my_mail = ......
# 发件人邮箱
from_user = "test@xxx.xxx"
# 收件邮箱
to_user = "test2@xxx.xxx"
# 发送邮件
smtp_server.send_message(my_mail, from_addr=from_user, to_addrs=to_user)
封装邮件发送方法
像上面这样一步步构造邮件、发送邮件,写一次还好,经常需要这样写的话还是有点繁琐的,所以我们来给它稍微封装一下,以后直接调用即可:
import smtplib
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
class MailSender(object):
"""
邮件发送器,封装smtp发送邮件的常用操作
"""
def __init__(self, user: str, password: str, host: str, port: int):
"""
初始化smtp服务器连接
:param user: smtp用户名(邮箱)
:param password: smtp登录密码
:param host: smtp服务器地址
:param port: smtp服务器端口,仅能使用25、465和587
"""
self.__user = user
# 连接到smtp服务器,限制只允许使用25、465、587这三个端口
if port == 25:
self.__smtp_server = smtplib.SMTP(host=host, port=port)
elif port == 465:
self.__smtp_server = smtplib.SMTP_SSL(host=host, port=port)
elif port == 587:
self.__smtp_server = smtplib.SMTP(host=host, port=port)
self.__smtp_server.starttls()
else:
raise ValueError("Can only use port 25, 465 and 587")
# 登录smtp服务器
self.__smtp_server.login(user=user, password=password)
def send_text(self, to_user: str, subject: str, content: str):
"""
发送纯文本邮件
:param to_user: 收件人邮箱
:param subject: 邮件主题
:param content: 邮件正文
"""
# 构造邮件
msg = MIMEText(content, _subtype="plain", _charset="utf-8")
# 添加发件人
msg["From"] = self.__user
# 添加收件人
msg["To"] = to_user
# 添加邮件主题
msg["subject"] = subject
# 发送邮件
self.__smtp_server.send_message(msg, from_addr=self.__user, to_addrs=to_user)
def send_html(self, to_user: str, subject: str, content: str):
"""
发送html邮件
:param to_user: 收件人邮箱
:param subject: 邮件主题
:param content: 邮件正文
"""
# 构造邮件
msg = MIMEText(content, _subtype="html", _charset="utf-8")
# 添加发件人
msg["From"] = self.__user
# 添加收件人
msg["To"] = to_user
# 添加邮件主题
msg["subject"] = subject
# 发送邮件
self.__smtp_server.send_message(msg, from_addr=self.__user, to_addrs=to_user)
def send_file(
self,
to_user: str,
file_path: str,
file_name: str,
subject: str = "",
content: str = "",
):
"""
发送带附件的邮件
:param to_user: 收件人邮箱
:param file_path: 附件路径
:param file_name: 附件在邮件中显示的文件名
:param subject: 邮件主题,默认为空字符串
:param content: 邮件正文,默认为空字符串
"""
# 读取附件内容
file_content = open(file_path, "rb").read()
# 构造一封多组件的邮件
msg = MIMEMultipart()
# 往多组件邮件中加入文本内容
text_msg = MIMEText(content, _subtype="plain", _charset="utf-8")
msg.attach(text_msg)
# 往多组件邮件中加入附件
file_msg = MIMEApplication(file_content)
file_msg.add_header("content-disposition", "attachment", filename=file_name)
msg.attach(file_msg)
# 添加发件人
msg["From"] = self.__user
# 添加收件人
msg["To"] = to_user
# 添加邮件主题
msg["subject"] = subject
# 发送邮件
self.__smtp_server.send_message(msg, from_addr=self.__user, to_addrs=to_user)
调用时只需要先实例化一个 MailSender()
对象,然后就可以使用对应的 send 函数来发送邮件了:
if __name__ == "__main__":
test_text_msg = "纯文本邮件发送测试"
test_html_msg = """
<p>html邮件发送测试</p>
<p><a href="https://xirikm.net/">这是一个链接</a></p>
"""
attachment_file_path = "xxx/xxx.txt" # 附件的文件路径
attach_msg = "带附件的邮件发送测试"
sender = MailSender("test@xxx.xxx", "test_password", "smtp.xxx.xxx", 587)
sender.send_text("test2@xxx.xxx", "纯文本邮件", test_text_msg)
sender.send_html("test2@xxx.xxx", "html邮件", test_html_msg)
sender.send_file("test2@xxx.xxx", attachment_file_path, "xxx.txt", "带附件的邮件", attach_msg)
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!