一、发邮件这件事,为什么特别适合自动化
办公场景里,很多重复通知类工作,看起来简单,做起来却特别消耗人。
比如:
每天发日报 每周发周报 给客户批量发通知 给员工发提醒 给名单中的人发送结果 给不同部门发送不同附件 给即将到期的客户发续约提醒 给考试通过或未通过的人发不同内容的邮件
这些事情真正麻烦的地方,不是“不会发”,而是:
收件人很多 内容重复但不完全一样 经常带附件 时间要求固定 不能发错人 不能漏发 最好还能留痕
只要这种工作高频重复,Python 就很适合介入。 因为邮件本身就是高度规则化的事情:
有收件人 有标题 有正文 有附件 有固定发送逻辑
程序最擅长的,恰恰就是这种规则明确、重复执行的任务。
二、自动发邮件,到底自动化的是什么
很多人一想到自动发邮件,会以为就是“让程序帮我点发送按钮”。 其实真正自动化的,通常是整条通知流程。
比如:
从 Excel 里读取名单 根据名单拼接不同正文 自动判断该发给谁 自动生成邮件标题 自动附带对应附件 自动发送 自动记录发送结果
也就是说,自动发邮件不是一个孤立动作,而是常常和数据读取、内容生成、附件匹配、结果记录连在一起。
所以你要先建立一个认识:
邮件自动化不是“发信技巧”,而是“通知流程自动化”。
三、为什么 Python 特别适合做邮件自动化
因为它刚好具备这条链路里最需要的能力:
能读数据 能处理文本 能循环批量 能操作文件 能按规则拼内容 能接邮件协议 还能和 Excel、Word、PDF 串起来
举个特别典型的流程:
Excel 里有客户名单 Python 读取名单 根据客户姓名、到期时间、产品名称拼接提醒内容 把对应合同 PDF 作为附件 自动发到每个客户邮箱
这个过程里,Python 几乎每一步都能接上。 这就是它特别适合做邮件自动化的原因。
四、自动发邮件最基础的技术路线是什么
在 Python 里,最经典的基础方式是用标准库里的:
smtplib
配合:
email
你可以简单理解成:
smtplib 负责连接邮件服务器并发送email 负责组织邮件内容,比如标题、正文、附件
所以如果只看最基础原理,自动发邮件其实就是三步:
先写好邮件 再连上发信服务器 最后发送出去
很多人一看到“邮件服务器”这几个字就紧张。 其实入门阶段,你不用先把协议细节全吃透。 你只要知道:程序不是凭空把邮件扔出去的,而是通过邮件服务商提供的 SMTP 通道发送。
五、先认识两个关键词:SMTP 和邮箱授权码
如果你想让 Python 自动发邮件,通常会遇到两个词。
第一个,SMTP。 它可以理解成发邮件时常用的服务器协议。 程序要发邮件,通常要通过 SMTP 服务器。
第二个,授权码。 很多邮箱为了安全,不允许你直接拿网页登录密码去做程序化发送。 它会要求你单独开启 SMTP 服务,然后生成一个“授权码”,专门给第三方程序使用。
这个机制非常重要,因为它更安全。 你程序里通常不会写你的网页登录密码,而是写授权码。
所以以后你做邮件自动化时,经常会出现这样的配置:
邮箱地址 SMTP 服务器地址 端口 授权码
这几项通常就是程序发信的核心配置。
六、一个最基础的自动发邮件示例
我们先看最简单的纯文本邮件写法。 这段代码的重点不是让你立刻去发,而是先看懂结构。
import smtplibfrom email.mime.text import MIMETextsender = "你的邮箱@example.com"password = "你的授权码"receiver = "收件人@example.com"msg = MIMEText("这是一封测试邮件。", "plain", "utf-8")msg["Subject"] = "测试邮件"msg["From"] = sendermsg["To"] = receiverserver = smtplib.SMTP_SSL("smtp.example.com", 465)server.login(sender, password)server.sendmail(sender, [receiver], msg.as_string())server.quit()
你可以把它拆开理解。
先定义发件人、授权码、收件人。 然后构造一封邮件对象。 标题、发件人、收件人都填进去。 再连接 SMTP 服务器。 登录。 发送。 最后断开连接。
虽然这段代码里有一些你还没见过的对象名,但整体逻辑其实并不复杂。
七、MIMEText 是什么,为什么正文要这样写
这一句:
msg = MIMEText("这是一封测试邮件。", "plain", "utf-8")
本质上是在创建“邮件正文对象”。
参数的含义可以先这样理解:
第一个参数,是正文内容 第二个参数,plain 表示纯文本 第三个参数,表示编码
也就是说,这是一封“普通文本邮件”。
如果你只是发简单通知,纯文本通常已经够用。 比如提醒、日报、状态通知、系统告警、名单结果,这些都很适合纯文本邮件。
八、为什么 SMTP_SSL 经常出现
你会发现很多示例都写成:
smtplib.SMTP_SSL(...)
这是因为很多邮箱服务商要求通过加密连接发信。 入门阶段你先记一个直觉就够:
如果邮箱服务商要求 SSL 加密方式,那通常就会用 SMTP_SSL。
至于服务器地址和端口,则要看你的邮箱服务商提供的配置。 不同服务商的 SMTP 地址和端口可能不同,所以真实使用时,一定要参考你所用邮箱的官方说明。
这里有一个很关键的实战习惯:
邮件配置不要死记别人的示例,要以自己邮箱服务商的说明为准。
九、自动发邮件最常见的第一个升级:批量发送
单发一封邮件没什么稀奇。 真正有价值的是批量发。
比如你有一份名单:
receivers = ["a@example.com","b@example.com","c@example.com"]
然后逐个发送:
import smtplibfrom email.mime.text import MIMETextsender = "你的邮箱@example.com"password = "你的授权码"receivers = ["a@example.com","b@example.com","c@example.com"]server = smtplib.SMTP_SSL("smtp.example.com", 465)server.login(sender, password)for receiver in receivers: msg = MIMEText("这是一封批量测试邮件。", "plain", "utf-8") msg["Subject"] = "批量通知" msg["From"] = sender msg["To"] = receiver server.sendmail(sender, [receiver], msg.as_string()) print(f"{receiver} 发送完成")server.quit()
这就已经是最基础的批量发信模型了。
这里有一个很实用的点: 连接服务器和登录只做一次。 然后在循环里重复发。 这样比每发一封都重新连接更合理。
十、真正有价值的批量发送,通常不是一封模板发给所有人
真实办公里,很多批量邮件并不是“同一封原样群发”,而是:
收件人不同 内容也有一点点不同
比如:
称呼不同 成绩不同 合同到期时间不同 待办事项不同 附件不同
这时候就要做个性化内容拼接。
例如:
import smtplibfrom email.mime.text import MIMETextsender = "你的邮箱@example.com"password = "你的授权码"users = [ {"姓名": "张三", "邮箱": "zhangsan@example.com", "成绩": 88}, {"姓名": "李四", "邮箱": "lisi@example.com", "成绩": 92}, {"姓名": "王五", "邮箱": "wangwu@example.com", "成绩": 76},]server = smtplib.SMTP_SSL("smtp.example.com", 465)server.login(sender, password)for user in users: content = f"{user['姓名']},你好。\n你的本次成绩为 {user['成绩']} 分。" msg = MIMEText(content, "plain", "utf-8") msg["Subject"] = "成绩通知" msg["From"] = sender msg["To"] = user["邮箱"] server.sendmail(sender, [user["邮箱"]], msg.as_string()) print(f"{user['姓名']} 发送完成")server.quit()
这个思路非常重要。 因为很多邮件自动化真正的价值就在于:
一套模板 多份不同内容 按名单自动生成并发送
十一、如果名单在 Excel 里,邮件自动化就更像真实场景了
现实里,名单通常不会手写在代码里,而是来自 Excel。
比如:
姓名 邮箱 部门 成绩 通知内容 附件路径
都在表格里。 这时你就可以把 Pandas 接进来:
import pandas as pdimport smtplibfrom email.mime.text import MIMETextsender = "你的邮箱@example.com"password = "你的授权码"df = pd.read_excel("邮件名单.xlsx")server = smtplib.SMTP_SSL("smtp.example.com", 465)server.login(sender, password)for _, row in df.iterrows(): content = f"{row['姓名']},你好。\n你所在部门是 {row['部门']}。" msg = MIMEText(content, "plain", "utf-8") msg["Subject"] = "部门通知" msg["From"] = sender msg["To"] = row["邮箱"] server.sendmail(sender, [row["邮箱"]], msg.as_string()) print(f"{row['姓名']} 发送完成")server.quit()
这就是非常典型的办公自动化链路:
Excel 作为数据源 Python 读取名单 按行拼接内容 自动发出
一旦这条链路打通,很多通知类工作都会被你接住。
十二、HTML 邮件:让内容更像正式通知
纯文本邮件够用,但有时不够好看。 比如你想让标题加粗、某些字段换颜色、内容更像正式通知。 这时可以发 HTML 邮件。
import smtplibfrom email.mime.text import MIMETextsender = "你的邮箱@example.com"password = "你的授权码"receiver = "收件人@example.com"html_content = """<h2>成绩通知</h2><p>张三,你好。</p><p>你的本次成绩为 <b>88</b> 分。</p>"""msg = MIMEText(html_content, "html", "utf-8")msg["Subject"] = "成绩通知"msg["From"] = sendermsg["To"] = receiverserver = smtplib.SMTP_SSL("smtp.example.com", 465)server.login(sender, password)server.sendmail(sender, [receiver], msg.as_string())server.quit()
这样收到的邮件通常会更接近“排版过的通知”。 办公中常见的场景有:
系统提醒 日报周报 审批通知 结果通知 活动邮件
不过要注意,HTML 邮件虽然更漂亮,但也更容易写复杂。 入门阶段先把纯文本发稳,再慢慢升级会更靠谱。
十三、自动发附件,是办公邮件自动化里的关键能力
很多正式邮件不是只发正文,还得带附件。 比如:
日报 Excel 报告 PDF 名单 Word 截图图片 合同附件
这时候就不能只用最简单的 MIMEText 了,通常要用多部分邮件结构。
下面看一个基础示例:
import smtplibfrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartfrom email.mime.application import MIMEApplicationsender = "你的邮箱@example.com"password = "你的授权码"receiver = "收件人@example.com"msg = MIMEMultipart()msg["Subject"] = "附件测试"msg["From"] = sendermsg["To"] = receivertext = MIMEText("请查收附件。", "plain", "utf-8")msg.attach(text)with open("报告.pdf", "rb") as f: attachment = MIMEApplication(f.read(), _subtype="pdf") attachment.add_header("Content-Disposition", "attachment", filename="报告.pdf") msg.attach(attachment)server = smtplib.SMTP_SSL("smtp.example.com", 465)server.login(sender, password)server.sendmail(sender, [receiver], msg.as_string())server.quit()
你可以先理解成:
先创建一个“可包含多个部分”的邮件对象 正文是一部分 附件也是一部分 最后一起发出去
这在办公自动化里非常重要。 因为很多邮件通知真正的核心,其实是附件,而不是正文。
十四、批量发送不同附件,才是真实办公里的高频场景
比如你要给每个客户发送各自的对账单 PDF。 这时候每个人的邮件内容和附件路径都可能不同。
假设 Excel 里有:
姓名 邮箱 附件路径
那思路通常就是:
按行读数据 按行拼内容 按行取附件 逐个发送
例如:
import pandas as pdimport smtplibfrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartfrom email.mime.application import MIMEApplicationsender = "你的邮箱@example.com"password = "你的授权码"df = pd.read_excel("发送名单.xlsx")server = smtplib.SMTP_SSL("smtp.example.com", 465)server.login(sender, password)for _, row in df.iterrows(): msg = MIMEMultipart() msg["Subject"] = "请查收对账单" msg["From"] = sender msg["To"] = row["邮箱"] content = MIMEText(f"{row['姓名']},你好。\n请查收本月对账单。", "plain", "utf-8") msg.attach(content)with open(row["附件路径"], "rb") as f: attachment = MIMEApplication(f.read()) attachment.add_header("Content-Disposition","attachment", filename=row["附件路径"].split("/")[-1] ) msg.attach(attachment) server.sendmail(sender, [row["邮箱"]], msg.as_string()) print(f"{row['姓名']} 发送完成")server.quit()
这类脚本在实际办公中价值非常高,因为它能把“逐个邮件 + 不同附件”的繁琐流程一把接走。
十五、自动发邮件,不只是会发,还要会控风险
这是非常重要的一点。
邮件自动化最怕的不是写不出来,而是:
发错人 发漏了 重复发 附件发错 正文变量错位 一次发太多被限流 发了一半报错却没记录
所以你以后真正做邮件自动化,不能只盯着“怎么发出去”,还要盯着:
怎么发得稳 怎么发得可追踪 怎么降低误发风险
这也是自动化脚本和“演示代码”最大的区别。
十六、最实用的安全习惯之一:先小范围测试
不要一上来就对几百个收件人正式开跑。 非常容易出事故。
正确做法通常是:
先发给自己 再发给一两个测试同事 确认正文、附件、标题都正常 确认名单读取没问题 确认变量没有错位 确认附件路径没有串 最后再正式批量执行
这个习惯非常值钱。 很多办公事故不是因为程序太复杂,而是因为没先做小样测试。
十七、第二个实用习惯:给发送结果做日志记录
批量邮件一多,光看终端输出很不稳。 更好的做法是把发送结果记下来。
比如:
results = []
每发一封,记录一下:
results.append({"姓名": row["姓名"],"邮箱": row["邮箱"],"状态": "成功"})
如果失败,就记失败原因:
results.append({"姓名": row["姓名"],"邮箱": row["邮箱"],"状态": f"失败:{e}"})
最后导出成 Excel:
pd.DataFrame(results).to_excel("发送结果.xlsx", index=False)
这样你就知道:
哪些发成功了 哪些失败了 失败原因大概是什么 是否需要补发
真实办公里,这类留痕非常重要。
十八、第三个实用习惯:给程序加异常处理
批量发信时,网络波动、附件缺失、邮箱地址错误、服务器拒绝,都可能发生。 如果你不加异常处理,一个失败就可能让整个流程中断。
一个更稳的写法通常是:
try: server.sendmail(sender, [row["邮箱"]], msg.as_string()) print(f"{row['姓名']} 发送成功")except Exception as e: print(f"{row['姓名']} 发送失败:{e}")
更完整一点:
results = []for _, row in df.iterrows():try:# 构造并发送邮件 server.sendmail(sender, [row["邮箱"]], msg.as_string()) results.append({"邮箱": row["邮箱"], "状态": "成功"})except Exception as e: results.append({"邮箱": row["邮箱"], "状态": f"失败:{e}"})
这样即使某一封失败,也不会把后面所有邮件都拖死。
十九、自动发邮件最常见的几个坑
1. 直接用网页登录密码
很多邮箱不允许,应该优先用授权码。
2. 邮箱地址格式有问题
名单里的邮箱有空格、拼错、为空,都会导致失败。 所以发送前最好先清洗数据。
3. 附件路径不对
文件不存在、路径写错、文件名乱码,都是常见问题。
4. 一次发太快、太多
有些服务商会限流。 必要时要控制节奏,比如两封之间稍微停一下。
5. 没先测试就正式群发
这是最危险的习惯之一。
6. 变量替换错位
比如张三收到李四的成绩,王五收到别人的附件,这种事故特别致命。 所以名单、正文、附件要一一对应核对。
二十、什么时候自动发邮件特别值得做
你可以先抓几个典型场景。
第一,固定周期通知。 比如日报、周报、月报、提醒邮件。
第二,名单类通知。 比如成绩通知、审核结果通知、报名结果通知。
第三,合同和账单类发送。 比如续约提醒、账单发送、回执发送。
第四,系统和运营辅助通知。 比如活动提醒、异常报警、状态变化通知。
这些场景都有一个共同点:
规则明确 格式固定 频率较高 人工操作容易出错
只要符合这几个条件,邮件自动化往往就特别划算。
二十一、一个更贴近实战的小案例
假设现在有一个 Excel 文件,里面有:
姓名 邮箱 部门
你要给每个人发部门通知。 代码思路大致如下:
import pandas as pdimport smtplibfrom email.mime.text import MIMETextsender = "你的邮箱@example.com"password = "你的授权码"df = pd.read_excel("部门名单.xlsx")results = []server = smtplib.SMTP_SSL("smtp.example.com", 465)server.login(sender, password)for _, row in df.iterrows():try: content = f"{row['姓名']},你好。\n你所在的部门是 {row['部门']},请查收本次通知。" msg = MIMEText(content, "plain", "utf-8") msg["Subject"] = "部门通知" msg["From"] = sender msg["To"] = row["邮箱"] server.sendmail(sender, [row["邮箱"]], msg.as_string()) results.append({"姓名": row["姓名"],"邮箱": row["邮箱"],"状态": "发送成功" })except Exception as e: results.append({"姓名": row["姓名"],"邮箱": row["邮箱"],"状态": f"发送失败:{e}" })server.quit()pd.DataFrame(results).to_excel("邮件发送结果.xlsx", index=False)
这段代码已经具备了邮件自动化里很关键的几件事:
从 Excel 读名单 个性化拼正文 批量发送 记录结果 导出发送日志
这已经不是“玩具代码”了,而是非常接近真实办公脚本。
二十二、这一章最该带走的核心思路
自动发邮件不是孤立技术,而是通知流程自动化。 邮件只是最后一步,前面往往还连着:
数据读取 变量拼接 附件匹配 结果留痕
你以后做邮件自动化时,最好先这样思考:
收件人名单从哪来 正文哪些内容是固定的,哪些是变量 附件是否统一,还是一人一份 发完后要不要记录结果 是否要控制发送节奏 是否需要先小范围测试
这套思路一旦建立起来,你会发现很多邮件类重复劳动都能被程序接手。
二十三、本章要点整理
Python 自动发邮件最常见的基础方式是 smtplib 配合 email 模块。smtplib 负责连接邮件服务器并发送,email 负责组织标题、正文和附件。 真实使用中,通常需要邮箱地址、SMTP 服务器地址、端口和授权码。 纯文本邮件可以用 MIMEText 构造,带附件邮件通常用 MIMEMultipart。 批量邮件自动化常常与 Excel 数据源结合,按名单逐行拼接个性化内容。 真实办公里,邮件自动化的重点不只是能发出去,还包括附件对应、异常处理、日志记录和风险控制。 先小范围测试、记录发送结果、避免误发,是非常重要的实战习惯。