使用smtplib发邮件需先启用SMTP服务并用授权码/OAuth2认证,强制TLS加密,发件人与登录账号一致;图表须用MIMEImage嵌入并cid引用;定时任务推荐cron或APScheduler;时区统一设为Asia/Shanghai。

用 smtplib 发邮件前必须处理好 SMTP 认证和 TLS
直接调用 smtplib.SMTP() 连接邮箱服务器大概率失败,因为现代邮箱(如 Gmail、Outlook、QQ 邮箱)默认禁用明文协议,且要求 OAuth2 或应用专用密码。用账号密码直连会触发「登录被拒绝」或「应用未获授权」错误。
实操建议:
- QQ 邮箱/163 邮箱需在网页端开启「SMTP 服务」,并生成「授权码」代替登录密码;Gmail 则必须用 App Password(需已开启两步验证)
- 务必使用
smtplib.SMTP_SSL()或先starttls()升级连接,否则认证会失败 - 发件人地址必须与登录账号一致,否则多数服务商拒发(例如用
user@163.com登录,From字段就不能写成report@xxx.com)
把 Matplotlib 图表嵌入邮件正文要用 MIMEImage + cid 引用
直接把图表路径写进 HTML 的 是无效的——邮件客户端根本读不到本地文件。必须将图像二进制内容作为附件嵌入,并用 
Content-ID(即 cid)在 HTML 中引用。
关键步骤:
- 用
io.BytesIO捕获plt.savefig(..., format='png', bbox_inches='tight')输出,避免写磁盘 - 创建
MIMEImage对象,调用add_header('Content-ID', '设置唯一 cid') - HTML 正文中对应位置写
,注意前后尖括号不能省 - 别漏掉
msg.attach(image_part),否则 cid 找不到资源,显示为红叉
定时执行早报脚本别只靠 time.sleep() 硬等
用 while True: time.sleep(3600); if now.hour == 8: send_report() 看似简单,但进程挂掉就停摆,系统重启后也不会自启,也不支持错峰重试或日志追踪。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
更可靠的做法:
- Linux/macOS 用
cron:添加一行0 8 * * * /usr/bin/python3 /path/to/morning_report.py >> /var/log/morning_report.log 2>&1 - Windows 用任务计划程序,触发器设为「每天上午 8:00」,操作选「启动程序」→
python.exe,参数填脚本绝对路径 - 如果必须用 Python 调度(比如要动态判断节假日),推荐
APScheduler,而不是手写轮询;注意设置coalesce=True和max_instances=1防重复
早报内容动态生成时,datetime 和时区最容易出错
脚本跑在服务器上,若服务器时区不是东八区(如 Docker 默认 UTC),datetime.now().hour 永远不是 8,导致永远不发;或者图表标题写成「2024-04-05 00:30」这种非人类可读时间。
稳妥写法:
- 显式指定时区:
from datetime import datetime; import pytz; beijing = pytz.timezone('Asia/Shanghai'); now = datetime.now(beijing) - 图表标题、邮件主题里的日期统一用
now.strftime('%m月%d日 %H:%M'),避免strftime('%Y-%m-%d')这种带年份的冗余格式 - 数据查询范围别写死,比如「昨日数据」应算
(now - timedelta(days=1)).date(),而不是date.today() - timedelta(days=1)(后者不带时区,可能跨天偏差)
真正麻烦的从来不是画图或发信,而是让整个链路在凌晨三点服务器自动重启后还能准时跑起来——认证过期、临时目录没权限、数据库连接池耗尽、甚至 matplotlib 的 Agg 后端没设都会让脚本静默失败。建议第一次部署后,手动改个时间提前测试,并把关键步骤(登录成功、图表生成、邮件发送返回码)全打到日志里。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










