邮件自动发送:Python 当你的私人秘书
本文基于 Python 3.9+,涉及库:
yagmail、smtplib(内置)。阅读时间约 10 分钟。安装依赖:
pip install yagmail
周五下午 5 点的"酷刑"
周五下午 4:55,办公室里弥漫着一种诡异的紧张感。
阿明盯着屏幕右下角的时间,心里默默祈祷:“今天千万别有事,让我准时下班……”
4:58,手机响了。老板在群里@他:“阿明,周报发一下。”
阿明叹了口气,打开邮箱,新建邮件:
- 收件人:boss@company.com
- 抄送:all-team@company.com
- 主题:【周报】阿明-第 3 周工作总结
- 正文:
各位领导、同事: 本周工作完成情况: 1. 完成销售数据汇总报表 2. 整理产品图片 300 张 3. 协助市场部处理 Excel 数据 下周工作计划: 1. 继续优化数据报表流程 2. 配合技术部完成系统测试 遇到的问题: 暂无 以上,请审阅。 阿明 2024-01-19阿明检查了一遍,点击发送。5:15,终于走出了公司大门。
这种情况,每周五上演一次。内容格式几乎一样,就几条事项变一变。
有一次阿明请假提前走了,忘了发周报。周一早上被老板叫进办公室:“上周五的周报呢?”
阿明委屈:“我请假了……”
"请假了周报就不发了?"老板皱眉,“这个习惯不好。”
阿明回到工位,跟老张吐槽:“张哥,这周报每周五固定发,内容都差不多,就不能自动发吗?”
老张正在收拾包准备下班,闻言笑了:“这种固定格式的活儿,让 Python 替你发啊。”
第一步:认识yagmail——发邮件的"一键启动"
“阿明,你知道 Python 发邮件有几种方式吗?”
“不知道……”
“两种。一种是原生的smtplib,像手动挡——能控制每个细节,但代码多、配置烦。”
“另一种是yagmail,像自动挡——一行代码搞定,你只管说’发给谁、说什么’,剩下的它帮你办。”
老张边说边写:
pipinstallyagmail最简示例:一行发邮件
importyagmail# 连接邮箱(以 QQ 邮箱为例)yag=yagmail.SMTP(user="你的邮箱@qq.com",password="授权码",host="smtp.qq.com")# 发送邮件yag.send(to="收件人@company.com",subject="测试邮件",contents="这是一封来自 Python 的测试邮件!")print("✅ 邮件已发送!")"就这么简单?"阿明不敢相信。
"就这么简单。"老张点头,"但有个前提——你得先拿到邮箱的授权码,不是登录密码。
第二步:获取邮箱授权码
“现在大部分邮箱(QQ、163、Gmail)都不让直接用密码登录 SMTP,要用授权码——相当于给第三方应用的一个’临时通行证’。”
老张在纸上画了个流程:
QQ 邮箱获取授权码:
- 登录 QQ 邮箱网页版
- 点击顶部【设置】→【账户】
- 找到【POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务】
- 开启【IMAP/SMTP服务】
- 按提示发短信验证,拿到16 位授权码
163 邮箱类似,在设置里找 SMTP 授权码。
Gmail需要开启【两步验证】,然后生成【应用专用密码】。
“拿到授权码后,填到代码里的password参数,别填你的真实密码。”
第三步:实战——自动发送周报
“来,咱们写一个能实际跑起来的周报邮件脚本。”
importyagmailfromdatetimeimportdatetime# ========== 配置区 ==========SENDER="你的邮箱@qq.com"# 发件人邮箱AUTH_CODE="你的16位授权码"# 授权码RECEIVERS=["boss@company.com","all-team@company.com"]# 收件人列表# 本周工作内容(实际可以从文件/数据库读取)this_week=["完成销售数据汇总报表(5 个部门)","整理产品图片 300 张,统一命名规范","协助市场部处理 Excel 数据清洗",]# 下周计划next_week=["继续优化数据报表自动化流程","配合技术部完成系统测试",]# ========== 生成邮件内容 ==========today=datetime.now().strftime("%Y-%m-%d")week_num=datetime.now().isocalendar()[1]# 获取本周是第几周subject=f"【周报】阿明-第{week_num}周工作总结"body=f"""各位领导、同事: 本周工作完成情况:{chr(10).join(f"{i+1}.{item}"fori,iteminenumerate(this_week))}下周工作计划:{chr(10).join(f"{i+1}.{item}"fori,iteminenumerate(next_week))}遇到的问题: 暂无 以上,请审阅。 阿明{today}"""# ========== 发送邮件 ==========yag=yagmail.SMTP(user=SENDER,password=AUTH_CODE,host="smtp.qq.com")yag.send(to=RECEIVERS,subject=subject,contents=body,)print(f"✅ 周报已发送!时间:{today}")阿明看完,恍然大悟:“原来邮件内容可以用 Python 字符串动态生成!”
“对。this_week和next_week是列表,你可以从 Excel、数据库、甚至钉钉审批里读取。”
“chr(10)是啥?”
“换行符。join的时候每个事项前面加序号,自动换行。”
第四步:发送附件 + HTML 正文
“光发文字不够吧?周报是不是还要带附件?比如 Excel 报表?”
阿明点头:“对!上周的汇总表要附在邮件里。”
“来,加附件和 HTML 格式。”
importyagmail yag=yagmail.SMTP(user="你的邮箱@qq.com",password="授权码",host="smtp.qq.com")# HTML 正文(更美观)html_body=""" <h2>本周工作汇报</h2> <p>各位领导、同事:</p> <p>本周主要完成以下工作:</p> <ul> <li>完成销售数据汇总报表(5 个部门)</li> <li>整理产品图片 300 张</li> <li>协助市场部处理 Excel 数据</li> </ul> <p>详细数据请查看附件。</p> <br> <p style="color: gray;">阿明<br>2024-01-19</p> """yag.send(to="boss@company.com",subject="【周报】阿明-第 3 周工作总结(含附件)",contents=html_body,# HTML 正文attachments=["1月汇总报表_已美化.xlsx","产品图片清单.pdf"],# 附件列表)print("✅ 带附件的 HTML 邮件已发送!")“attachments参数传一个列表,可以附多个文件。contents传 HTML 字符串,邮件客户端会自动渲染。”
阿明感叹:“这比我在邮箱客户端里排版方便多了……”
第五步:邮件模板化——用 Jinja2 渲染
“阿明,你每周改的内容就那几项,但每次都要改代码里的列表,不麻烦吗?”
“是有点……”
“来,咱们引入模板引擎——Jinja2。”
pipinstalljinja2“原理很简单:把邮件内容写成一个模板,留几个’填空区’,每周只填数据,不用改格式。”
创建模板文件weekly_report_template.html
<h2>本周工作汇报</h2><p>各位领导、同事:</p><h3>本周完成</h3><ul>{% for item in this_week %}<li>{{ item }}</li>{% endfor %}</ul><h3>下周计划</h3><ul>{% for item in next_week %}<li>{{ item }}</li>{% endfor %}</ul><h3>遇到的问题</h3><p>{{ issues }}</p><pstyle="color:gray;margin-top:30px;">{{ name }}<br>{{ date }}</p>“{% for item in this_week %}这种语法叫模板标签,意思是’遍历 this_week 列表,每个元素生成一个<li>'。”
“{{ item }}是变量插值,把数据填进去。”
Python 代码:渲染模板并发送
importyagmailfromjinja2importTemplatefromdatetimeimportdatetime# 读取模板withopen("weekly_report_template.html","r",encoding="utf-8")asf:template=Template(f.read())# 每周只改这里的数据!data={"this_week":["完成销售数据汇总报表","整理产品图片 300 张","协助市场部处理数据",],"next_week":["优化报表自动化流程","配合技术部系统测试",],"issues":"暂无","name":"阿明","date":datetime.now().strftime("%Y-%m-%d"),}# 渲染 HTMLhtml_body=template.render(data)# 发送yag=yagmail.SMTP(user="你的邮箱",password="授权码",host="smtp.qq.com")yag.send(to="boss@company.com",subject=f"【周报】阿明-{datetime.now().strftime('%m月第%W周')}工作总结",contents=html_body,attachments=["1月汇总报表.xlsx"],)print("✅ 模板化周报已发送!")“看到没?每周只需要改data字典里的内容,邮件格式完全不动。”
“这就像你写了个’填空题模板’,每周填答案就行。”
阿明一拍大腿:“这个好!我下周就把这个跑起来!”
踩坑提醒:邮件发送的几个坑
老张放下手机,认真起来:
坑 1:授权码过期
“QQ 邮箱的授权码长期有效,但 163 邮箱的授权码可能一段时间后会失效,需要重新获取。”
“如果突然发不出去,先检查授权码是否还有效。”
坑 2:被当成垃圾邮件
“如果你频繁发邮件(比如一天几十封),或者附件太大,可能被对方邮箱拦截到垃圾箱。”
"解决办法:
- 控制发送频率
- 附件压缩后再发
- 邮件内容别太像广告(少用"免费"“优惠"等词)”
坑 3:SMTP 服务器连不上
“公司内网可能屏蔽了 25/465/587 端口,导致连不上 SMTP 服务器。”
"解决办法:
- 换端口试试(QQ 邮箱支持 465/587)
- 问 IT 部门是否有限制
- 用公司自己的邮件服务器"
坑 4:附件路径不对
“attachments里的路径要用绝对路径,或者确保 Python 脚本运行时的工作目录是对的。”
frompathlibimportPath# 用绝对路径,避免找不到文件attachment_path=Path(__file__).parent/"reports"/"1月汇总报表.xlsx"一句话总结
阿明把代码保存好,设置好每周五下午 4:30 的闹钟提醒自己运行脚本。
老张笑了:“别急,下篇教你定时任务,连闹钟都不需要,代码自己到点就跑。”
阿明:“???还能这样?”
老张留下一句话:
“写邮件就像填快递单,每次地址都一样,就内容变一点。Python 就是帮你填单的助理,你只需要说’发’,它帮你写完贴好邮票。模板化之后,你连填单都省了,改个数据就行。”
扩展思考
"今天学的是发周报。但邮件自动化的场景还有很多:
- 节日祝福:春节、中秋,给同事/客户群发祝福邮件(用模板换内容)
- 系统告警:服务器 CPU 过高,自动发邮件通知运维
- 数据日报:每天自动抓取数据,生成报告发邮件
- 批量通知:给 100 个客户发会议邀请(用循环 + 个性化变量)
"核心逻辑都一样:模板 + 数据 = 自动化内容。
下集预告
下一篇,老张教阿明用schedule和APScheduler设置定时任务——每天 8 点自动跑脚本,每周五下午自动发周报。阿明终于实现了"代码在跑,我在喝咖啡"。
记住:固定格式的邮件,用模板 + 数据自动化。别当填单机器,让 Python 当你的秘书。
你们公司有没有那种"每周固定发"的邮件?是什么内容?欢迎在评论区吐槽。