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