Python使用smtplib发送邮件
SSL、TLS和STARTTLS
-
由前文我们知道云服务器基本上不允许25端口对外通信,要对外发邮件只能考虑465和587端口。那么465和587端口有什么区别?这要先从SSL、TLS和STARTTLS的区别开始说。
-
SSL(Secure Socket Layer)是加密传输层,TLS(Transport Layer Security)是SSL的继承者和升级版,提供更好的安全性和性能。SSL有SSL v2、SSL v3两个版本,目前都不建议使用。TLS有TSL v1.0-v1.3,建议至少使用TLS v1.2。
-
TLS和STARTTLS两者关系不大,但更让人容易产生误解,原因是名字中都带有TLS。STARTTLS是升级非安全连接为安全连接的协议,并没有强制使用加密。当服务端支持时,客户端和服务端才协商将已经建立的连接升级到SSL或者TLS加密。
-
接着看465端口和587端口。我们知道25端口刚被设计出来时是用于转发邮件的,没有考虑认证、加密等问题。随着垃圾邮件泛滥、网络安全问题严重,MSA、ESMTP/SMTPS等概念和协议被设计出来。1997年465端口被注册用于加密方式(SMTPS)提交邮件,那时STARTTLS还没有捣腾出来。1998年STARTLS标准出炉,规定用587端口以STARTTLS方式提交邮件,465端口被吊销。然而许多客户端不支持STARTTLS,加上非常多邮件服务提供商都在使用465端口作为加密提交端口,于是465就一直这么被用到今天。
-
简单来说,465端口只支持加密传输,不符合互联网号码分配结构(The Internet Assigned Numbers Authority,IANA)的标准,但一直被使用和支持;587端口专门被设计用来提交邮件,传输可以加密也可以不加密。
# -*- coding=utf8 -*-
import smtplib
from email.mime.text import MIMEText
from email.header import Header
from email.utils import parseaddr, formataddr
class MailSender(object):
"""
邮件发送器,封装smtp发送邮件的常用操作
"""
def __init__(self, user, password, host, port):
"""
初始化smtp服务器连接
:param user: smtp 邮箱地址 如 <prefix@example.com> 的形式
:param password: smtp 登录密码
:param host: smtp 服务器地址
:param port: smtp 服务器端口,仅能使用25、465和587
25端口(明文传输)465端口(SSL 加密) 587端口(STARTTLS 加密)
"""
self.user = user
self.password = password
self.host = host
self.port = port
@staticmethod
def _format_addr(in_str):
name, addr = parseaddr(in_str)
return formataddr((Header(name, 'utf-8').encode(), addr))
def _format_receivers_addr(self, receivers):
ret = []
for receiver in receivers:
name, _ = receiver.split("@")
ret.append(self._format_addr("%s <%s>" % (name, receiver)))
return ";".join(ret)
def _create_smtp_obj(self):
# 1. 创建 smtp Object
if self.port == 25:
smtp_obj = smtplib.SMTP(self.host, self.port)
elif self.port == 465:
smtp_obj = smtplib.SMTP_SSL(self.host, self.port)
elif self.port == 587:
smtp_obj = smtplib.SMTP(self.host, self.port)
smtp_obj.starttls()
else:
raise ValueError("Can only use port 25, 465 and 587")
# 2. 登录到服务器
smtp_obj.login(self.user, self.password)
return smtp_obj
@staticmethod
def _create_msg(msg_str, msg_type="html"):
if msg_type not in ("plain", "html"):
raise ValueError(
'Error subtype, only "plain" and "html" can be used')
return MIMEText(msg_str, msg_type, "utf-8")
def send(self, receivers, subject, msg_str, msg_type="html"):
"""
发送邮件
:param receivers: 邮件接收者 如:["xxx@xxx.com", "xxx@xxx.com"]
:param subject: 邮件标题 如:"来自SMTP的问候……"
:param msg_str: 邮件内容
:param msg_type: 邮件内容类型 如:"plain", "html"
:return:
"""
send_name, _ = self.user.split("@")
msg = self._create_msg(msg_str, msg_type)
msg["From"] = self._format_addr('%s <%s>' % (send_name, self.user))
msg["To"] = self._format_receivers_addr(receivers)
msg["Subject"] = Header(subject, "utf-8")
try:
# 1. 创建 SMTP Object
smtp_obj = self._create_smtp_obj()
# 3. 发送
smtp_obj.sendmail(self.user, receivers, msg.as_string())
# 4. 退出
smtp_obj.quit()
print("success")
return True
except smtplib.SMTPException as e:
print("error: ", e)
return False
if __name__ == '__main__':
# ====================================================================
# 第三方 SMTP 服务
stmp_user = "test@163.com" # 邮箱地址
stmp_pass = "******" # 口令
stmp_host = "smtp.163.com" # 设置邮箱服务器
stmp_port = 465 # 25端口(明文传输)465端口(SSL 加密) 587端口(STARTTLS 加密)
send_to = ["xxx@xxx.com"]
# =====================================================================
mail_msg = """
<p>Python 邮件发送...</p>
<p><a>这是一个打不开的链接</a></p>
"""
mail_sender = MailSender(stmp_user, stmp_pass, stmp_host, stmp_port)
mail_sender.send(
receivers=send_to,
subject="来自SMTP的问候……",
msg_str=mail_msg,
msg_type="html")
参考文章:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017790702398272
https://www.runoob.com/python/python-email.html
https://blog.inkuang.com/2020/414/
https://juejin.cn/post/6844903615644057608#heading-1
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本