Python · 5 分钟阅读
`smtplib` & `email`:发送邮件
目录
- 1. 准备
- 2. 用
email.message.EmailMessage构造邮件 - 3. 发送:示例
- 4. send_message vs sendmail
- 5. 常见问题
- 6. 用
yagmail/emails等第三方库(可选) - 7. 小结
Python 发送邮件靠两个标准库:
smtplib:连接 SMTP 服务器、发 SMTP 命令。email:构造规范的邮件内容(文本 / HTML / 附件 / 标题 / 编码)。
⚠️ 永远不要把账号密码写死在源码里,用环境变量或配置文件。
现代邮箱(Gmail / Outlook / QQ)几乎都需要 TLS,明文 25 端口大多
已经废弃。
1. 准备
1.1 邮箱授权码
主流邮箱需要 授权码(不是登录密码):
- 163:设置 → POP3/SMTP/IMAP → 客户端授权密码
- QQ:设置 → 账户 → POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 → 开启并生成授权码
- Gmail:开启两步验证 → 应用专用密码
- Outlook:直接用账号密码(开启 SMTP)
1.2 服务器 / 端口
| 服务 | 主机 | 推荐端口(SSL) | 备选(STARTTLS) |
|---|---|---|---|
| 163 | smtp.163.com |
465 或 994 | 25 / 587 |
smtp.qq.com |
465 或 587 | — | |
| Gmail | smtp.gmail.com |
465 | 587 |
| Outlook | smtp.office365.com |
587 (STARTTLS) | — |
| 自建 | 视情况 | 465 | 587 |
端口 465 = SMTP over SSL(先 TLS 再 SMTP);端口 587 = STARTTLS
(先明文 SMTP,再starttls()升级)。生产环境二选一,绝不直接 25 明文。
2. 用 email.message.EmailMessage 构造邮件
from email.message import EmailMessage
msg = EmailMessage()
msg["Subject"] = "Test email"
msg["From"] = "python@example.com"
msg["To"] = "demo@example.com"
msg.set_content("这是一封纯文本测试邮件。")
不要再自己拼
From: ...\nTo: ...\n字符串——EmailMessage会处理
编码、换行、quoted-printable 等所有坑。
2.1 HTML 邮件 + 纯文本 fallback
msg.set_content("你的邮箱不支持 HTML,请用纯文本查看。")
msg.add_alternative("<h1>Hello</h1><p>这是一封 <b>HTML</b> 邮件。</p>", subtype="html")
2.2 附件
msg.add_attachment(
open("report.pdf", "rb").read(),
maintype="application",
subtype="pdf",
filename="report.pdf",
)
3. 发送:示例
import smtplib
import os
from email.message import EmailMessage
def make_message() -> EmailMessage:
msg = EmailMessage()
msg["Subject"] = "Test email from wlh"
msg["From"] = "python@example.com"
msg["To"] = "demo@example.com"
msg.set_content("this is a testing")
return msg
def send_via_ssl(msg: EmailMessage) -> None:
host = "smtp.163.com"
user = "python@example.com"
password = os.environ["SMTP_PASSWORD"] # 授权码
with smtplib.SMTP_SSL(host, 465, timeout=10) as s:
s.login(user, password)
s.send_message(msg)
def send_via_starttls(msg: EmailMessage) -> None:
host = "smtp.qq.com"
user = "python@example.com"
password = os.environ["SMTP_PASSWORD"]
with smtplib.SMTP(host, 587, timeout=10) as s:
s.ehlo()
s.starttls(context=None) # 升级为 TLS
s.login(user, password)
s.send_message(msg)
if __name__ == "__main__":
send_via_ssl(make_message())
with smtplib.SMTP(...)会自动quit(),不要忘记。
4. send_message vs sendmail
| 方法 | 说明 |
|---|---|
s.send_message(msg) |
推荐,接受 EmailMessage |
s.sendmail(from, to, str) |
接受字符串,已不推荐 |
sendmail仍能用,但你要自己处理 RFC 5322 格式;send_message帮你做
对的事情。
5. 常见问题
SMTPAuthenticationError:账号 / 授权码不对,或没启用 SMTP 服务。SMTPDataError554:被识别为垃圾邮件。检查 From / Subject / 内容;
减少链接/图片。- 中文标题乱码?
EmailMessage会自动用Header编码;如果你手写
字符串到msg["Subject"],要Subject = "=?utf-8?B?...?="。 - 收件人多个? 逗号分隔:
msg["To"] = "a@x.com, b@y.com"或
s.send_message(msg, to_addrs=["a@x.com", "b@y.com"])。 - 附件中文名乱码? 用
EmailMessage.add_attachment(filename="报告.pdf")
时不要自己编码,库已经处理;如果你手写字符串才会乱。 - 连接超时? 加
timeout=10;考虑公司网络是否要代理。 - SSL 报错:
smtplib.SMTP_SSL默认校验证书;certifi缺失或证书过期
时会抛ssl.SSLCertVerificationError。升级certifi或pip install --upgrade certifi。
6. 用 yagmail / emails 等第三方库(可选)
如果项目里频繁发邮件,可以省事:
yagmail:Gmail 专用,几行就能发带附件的邮件。emails/premailer:HTML 邮件 + 内联 CSS。flask-mail:Flask 框架集成。
简单脚本直接
smtplib + email即可,无需引入依赖。
7. 小结
smtplib连服务器 + 发 SMTP 命令;email拼装邮件内容。- 端口 465 SSL 或 587 STARTTLS 二选一,不要用 25 明文。
- 用
EmailMessage+send_message,别再手拼From: ...字符串。 - 账号 / 授权码用环境变量或
.env注入,不要写进源码。 - 收件人 / 主题 / 内容别像垃圾邮件,否则会被 554 拒发。