【Python】标准库email+smtplib:发送邮件
【Python】标准库email+smtplib:发送邮件
python中的标准库提供自动发送邮件的功能。标准库email库可以完成邮件的创建和解析,标准库smtplib库完成连接邮箱服务器并发送邮件。
标准库email:
创建邮件并解析,遵循RFC标准。RFC(Requests For Comments,请求评论,是一系列互联网相关的资料和技术文档)。官网:» RFC Editor
python3.6之前的email库,需要创建多个MIME对象(正文内容 MIMEText,附件文件 MIMEApplication,附件图片 MIMEImage等),并且手动调整header(email.header),繁琐而又易出错。注:MIME(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展),是一种标准、协议、规范。
python3.6之后的email库,引入了email.message.EmailMessage,仍然会创建完整的MIME对象树,但只需声明所需内容,python会自动完成所有底层的MIME结构、header信息、编码等,更方便更简洁,而且默认使用更国际化的策略policy。
| python3.6之前的email库 | python3.6之后的email库 | |
|---|---|---|
| 邮件实例化 |
from email.mime.multipart import MIMEMultipart mm=MIMEMultipart('related') |
from email.message import EmailMessage mm = EmailMessage() |
| 主题 |
from email.header import Header mm["Subject"] = Header("主题", 'utf-8').encode() |
mm["Subject"] = "主题" |
| 发送人 |
mm["From"] = '发件人' |
mm["From"] = '发件人' |
| 接收人 |
mm["To"] = f"收件人1, 收件人2" |
mm["To"] = f"收件人1, 收件人2" |
|
正文 (文本) |
from email.mime.text import MIMEText text = MIMEText('正文文本内容', "plain", "utf-8") mm.attach(text) |
mm.set_content('正文文本内容') |
|
正文 (html) |
html_text = MIMEText('正文html内容', "html", "utf-8") mm.attach(html_text) |
mm.add_alternative('''正文html内容''', subtype='html') |
|
附件 (txt文件) |
from email.mime.application import MIMEApplication attach = MIMEApplication(open(f'txt文件路径', 'rb').read(), 'utf-8') attach.add_header('Content-Disposition', 'attachment', filename='文件名') mm.attach(attach) |
with open(f'txt文件路径', 'rb') as f: mm.add_attachment(f.read(), maintype='application', subtype='octet-stream', filename='文件名') |
|
附件 (png图片) |
from email.mime.image import MIMEImage image = MIMEImage(open(f'png图片路径', 'rb').read()) image.add_header('Content-Disposition', 'attachment', filename='图片名') mm.attach(image) |
with open(f'png图片路径', 'rb') as f: mm.add_attachment(f.read(), maintype='image', subtype='png', filename='图片名') |
python3.6之后的email库,三个主要组件和一个控制组件:
- email.messasge:核心组件,其中主要是email.messasge.EmailMessage类。 创建邮件。邮件对象模型是对象树结构,即邮件及其MIME子组件组成树状结构,其中每一个节点都是EmailMessage对象,每个EmailMessage对象都符合MIME标准,也可以说每个EmailMessage对象都是MIME组件。
- email.parser:解析器。将邮件的序列化字节流转为EmailMessage对象树。
- email.generator:生成器。将EmailMessage对象转为序列化的字节流。
- email.policy:策略模块。每个EmailMessage、每个parser、每个generator都有对应的策略对象控制其行为。默认策略是更国际化的邮件标准(如 RFC5322 或6532)。

主要类:email.message.EmailMessage(...)
类 email.message.EmailMessage(policy=default)
注:不指定策略,则使用默认策略。
每一个EmailMessage对象都由头部header和有效负载payload即内容组成,
- 邮件头部:包括主题、收件人、接收人等,头部的结构类似于有序字典。
- 有效负载(内容):包括正文、附件等,结构是字符串或字节对象或EmailMessage对象列表(复合邮件,比如文本、附件)。
EmailMessage实例化对象(例如):mm=EmailMessage()
邮件各个部分:
1、邮件头部信息
邮件头部信息类似于有序字典。树结构的各个节点都有头部信息。
邮件主题:EmailMessage['Subject']
例如:mm["Subject"] = '邮件主题'
注:mm为EmailMessage实例化对象。
邮件发送人:EmailMessage['From']
例如:mm["From"] = '发件人'
注:mm为EmailMessage实例化对象。
邮件接收人:EmailMessage['To']
例如:mm["To"] = '收件人'
注:mm为EmailMessage实例化对象。若多个收件人则中间以逗号", "隔开。
2、有效负载(邮件正文内容)
邮件正文:
(文本内容):EmailMessage.set_content(...)
EmailMessage.set_content(*args, content_manager=None, **kw)
邮件的正文为文本内容。
例如:mm.set_content(f'邮件正文文本内容')
注:mm为EmailMessage实例化对象。
(html内容):EmailMessage.add_alternative(...)
EmailMessage.add_alternative(*args, content_manager=None, **kw)
邮件的正文为html格式的内容。
若邮件是复合邮件(邮件类型是multipart/alternative),则自动创建子节点,自动执行set_content()和attach()到子节点。
例如:mm.add_alternative(f''''邮件正文html内容'', subtype='html')
注:mm为EmailMessage实例化对象。
附件:EmailMessage.add_attachment(...)
EmailMessage.add_attachment(*args, content_manager=None, **kw)
给邮件添加附件。若邮件类型是multipart/alternative,则自动创建子节点,自动执行set_content()和attach()到子节点。
例如:(附件:TXT文件)
mm.add_attachment(打开的txt文件.read(), maintype='application', subtype='octet-stream', filename=文件名')
(附件:png图片)
mm.add_attachment(打开的图片.read(), maintype='image', subtype='png', filename='图片名')
注:mm为EmailMessage实例化对象。
查看树的对象结构:
方法一:遍历树EmailMessage.walk(),使用get_content_type方法查看每个节点的内容类型。
EmailMessage.walk()
以深度优先的顺序遍历树的所有节点。
例如:for part in mm.walk()
print(part.get_content_type()) # 打印节点的内容类型(message’s content type)
注:mm为EmailMessage实例化对象。
方法二:使用 email.iterators._structure(EmailMessage)
from email.iterators import _structure
_structure(mm)
注:mm为EmailMessage实例化对象。
其他参考:email.message: Representing an email message — Python 3.13.7 documentation
标准库smtplib:
定义了SMTP客户会话对象(简称SMTP对象),向具有SMTP或ESMTP监听守护进程的计算机发送邮件。
SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)。
本文以163邮箱为例。需开启POP3/SMTP服务并获取邮箱授权码(不是邮箱登录密码)。发送邮件需知道邮箱服务器主机地址和端口。
准备工作:
开启POP3/SMTP服务,获取邮箱授权码,获取邮箱服务器主机地址。均在邮箱界面的设置下的“POP3/SMTP/IMAP”中。
0、进入163邮箱 --> 点击:设置 --> 点击:POP3/SMTP/IMAP

1、开启POP3/SMTP服务

2、获取邮箱授权码
开启POP3/SMTP服务后,会提示授权码,需记住该授权码,有效期180天。
3、获取邮箱服务器主机地址
设置中POP3/SMTP/IMAP界面最下方提示中就有SMTP服务器地址。

4、服务器端口
很多邮箱都使用常用端口,SSL端口一般为465,TLS/STARTTLS端口一般为587,而端口25也是163邮箱支持的端口但不推荐(据说端口25经常被ISP封锁以防止垃圾邮件).。
注:SSL和TLS都是加密端口为了增强安全性。而SSL端口连接需要使用子类smtplib.SMTP_SSL。
涉及类:smtplib.SMTP(...)
类:smtplib.SMTP(host='', port=0, local_hostname=None, [timeout, ]source_address=None)
管理连接SMTP或ESMTP服务器。
注:host 邮箱服务器主机,port 邮箱服务器端口。
若使用加密端口(TLS/STARTTLS端口),需注意使用SMTP.starttls()方法。
子类:smtplib.SMTP_SSL(...)
子类:smtplib.SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None, [timeout, ]context=None, source_address=None)
管理连接SSL端口的SMTP服务器。
注:host 邮箱服务器主机,port 邮箱服务器端口。
若port未指定,则默认使用标准SSL端口465。
类的主要方法:
1、登录:SMTP.login(...)
SMTP.login(user, password, *, initial_response_ok=True)
在需要认证的SMTP服务器上登录邮箱。
注:user 邮箱,password 邮箱授权码(不是邮箱登录密码)。
2、发送邮件:SMTP.sendmail(...)
SMTP.sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())
参数:from_addr 发送邮箱。to_addrs 接收邮箱(多个邮箱使用列表)。 msg 发送内容(需是字符串含字节字符串)。
3、退出邮箱服务器:SMTP.quit()
SMTP.quit()
终止SMTP回话,关闭连接。
帮助:
标准库可以使用以下方法获取类和方法的使用说明文档。
1、官方文档:The Python Standard Library — Python 3.13.7 documentation
2、开始--> Python --> Python Manuals --> 搜索(例如):smtplib。

3、python解释器 --> 导入库(例如):import smtplib --> 使用help(...)获取类或方法的文档说明。

举例:
python3.6之后的email库 + smtplib 实现简单的邮件发送。
import smtplib
from datetime import datetime
from email.message import EmailMessage
def send_email():
mail_server = "smtp.163.com"
mail_receivers = ["收件人邮箱1", "收件人邮箱2"] # 自行修改
mail_sender = "发件人邮箱" # 自行修改
# 邮箱授权码(POP3/SMTP服务的授权码,有效期180天)
mail_password = "发件人邮箱授权码" # 自行修改
mm = EmailMessage()
# 邮件主题
mm["Subject"] = "新email库,邮件发送"
# 发件人
mm["From"] = mail_sender
# 收件人
mm["To"] = ", ".join(mail_receivers)
# # 邮件的文本内容
# now = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
# text_content = f"正文文本 {now}"
# mm.set_content(text_content)
# 邮件的HTML内容
html_content = f"""
<html>
<body>
<h1 style="color:blue;">正文html</h1>
<p>时间: {now}</p>
</body>
</html>
"""
mm.add_alternative(html_content, subtype='html')
# 附件1,传送文件:data.txt
with open(f'G:/Python/my_modules/data.txt', 'rb') as f:
mm.add_attachment(f.read(), maintype='application', subtype='octet-stream', filename='data.txt')
# 附件2,传送图片:do.png
with open(f'G:/Python/my_modules/do.png', 'rb') as f:
mm.add_attachment(f.read(), maintype='image', subtype='png', filename='do.png')
# # 查看邮件结构
# for part in mm.walk():
# print(part.get_content_type())
# # 或者
# from email.iterators import _structure
# _structure(mm)
# 发送邮件
try:
# 连接邮箱服务器
smtp = smtplib.SMTP_SSL(mail_server, 465)
# 登录邮箱
smtp.login(mail_sender, mail_password)
# 发送邮件
smtp.sendmail(mail_sender, mail_receivers, mm.as_string())
print("邮件发送成功")
# 关闭服务器
smtp.quit()
except Exception as e:
print("邮件发送失败")
print(e)
if __name__ == "__main__":
send_email()
python3.6之前的email库 + smtplib 实现简单的邮件发送。
import smtplib
from datetime import datetime
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.header import Header
def send_email():
mail_server = "smtp.163.com"
mail_receivers = ["收件人邮箱1", "收件人邮箱2"] # 自行修改
mail_sender = "发件人邮箱" # 自行修改
# 邮箱授权码(POP3/SMTP服务的授权码,有效期180天)
mail_password = "发件人邮箱授权码" # 自行修改
# 邮件头部内容
mm = MIMEMultipart('related')
# 邮件主题
mm["Subject"] = Header("Python SMTP 邮件发送", 'utf-8').encode()
# 发件人
mm["From"] = mail_sender
# 收件人
mm["To"] = f"{mail_receivers[0]}, {mail_receivers[1]}"
# # 邮件的正文内容
# now = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
# mail_content = f"邮件正文文本: {now}"
# text = MIMEText(mail_content, "plain", "utf-8")
# mm.attach(text)
# html内容
now = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
html_content = f"""
<html>
<body>
<h1 style="color:blue;">邮件html内容</h1>
<p>时间: {now}</p>
</body>
</html>
"""
html_text = MIMEText(html_content, "html", "utf-8")
mm.attach(html_text)
# 附件1,传送文件:data.txt
attach = MIMEApplication(open(f'G:/Python/my_modules/data.txt', 'rb').read(), 'utf-8')
attach.add_header('Content-Disposition', 'attachment', filename='data.txt')
mm.attach(attach)
# 附件2,传送图片:do.png
image = MIMEImage(open(f'G:/Python/my_modules/do.png', 'rb').read())
image.add_header('Content-Disposition', 'attachment', filename='do.png')
mm.attach(image)
# 发送邮件
try:
# 连接邮箱服务器
smtp = smtplib.SMTP_SSL(mail_server, 465)
# # 不报错,但不推荐,端口25经常被ISP封锁以防止垃圾邮件
# smtp = smtplib.SMTP(mail_server, 25)
# 使用587失败,Connection unexpectedly closed
# smtp = smtplib.SMTP(mail_server, 587)
# smtp.starttls() # 启用加密
# 登录邮箱
smtp.login(mail_sender, mail_password)
# 发送邮件
smtp.sendmail(mail_sender, mail_receivers, mm.as_string())
print("邮件发送成功")
# 关闭服务器
smtp.quit()
except Exception as e:
print("邮件发送失败")
print(e)
if __name__ == "__main__":
send_email()
更多推荐


所有评论(0)