从 Excel 读数据 → 生成个性化邮件内容 → 批量发送
- 2026-11-08 08:49:32
先想清楚:你要的是“文档”还是“邮件”
上一篇的邮件合并不是没用。如果你要生成的是** Word 附件**(比如工资条、录取通知书),然后发出去,合理路径是:Word 邮件合并生成文档 → 再用 Python 或 Outlook 批量发带附件的邮件。
但如果你要发的就是邮件正文本身——通知、提醒、个性化简报,不需要附件——那没必要绕道 Word。直接用 Python 从 Excel 读数据、拼正文、发信,一步到位。下面讲的也是这个场景。
需要准备的东西:一个 Excel 文件,包含收件人邮箱和要插入邮件的内容;一个发件邮箱的 SMTP 授权码(不是登录密码)。
脚本长什么样
核心依赖只有两个:openpyxl 读 Excel,smtplib + email 发邮件。后者是 Python 标准库,不用装。
import smtplibfrom email.mime.text import MIMETextfrom email.header import Headerimport openpyxl# ========== 配置区 ==========EXCEL_PATH = "recipients.xlsx"SMTP_SERVER = "smtp.qq.com" # QQ邮箱用这个,163是 smtp.163.comSMTP_PORT = 465 # SSL端口SENDER = "your_email@qq.com"AUTH_CODE = "你的授权码" # 不是QQ密码,是设置里生成的授权码# 邮件正文模板,用 {} 占位BODY_TEMPLATE = """{name} 你好:这是本月的数据简报,你的专属编号是 {code},请于 {deadline} 前确认。—— 自动发送,无需回复"""# ========== 读数据 ==========wb = openpyxl.load_workbook(EXCEL_PATH)ws = wb.active# 假设第一行是表头:姓名 | 邮箱 | 编号 | 截止日期rows = []for row in ws.iter_rows(min_row=2, values_only=True):if row[1] is None: # 邮箱为空就跳过continuerows.append({"name": row[0],"email": row[1],"code": row[2],"deadline": row[3]})print(f"共读取 {len(rows)} 条记录")# ========== 发邮件 ==========server = smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT)server.login(SENDER, AUTH_CODE)for item in rows:body = BODY_TEMPLATE.format(name=item["name"],code=item["code"],deadline=item["deadline"])msg = MIMEText(body, "plain", "utf-8")msg["Subject"] = Header(f"数据简报 - {item['name']}", "utf-8")msg["From"] = SENDERmsg["To"] = item["email"]try:server.sendmail(SENDER, [item["email"]], msg.as_string())print(f"✅ 已发送 → {item['name']} ({item['email']})")except Exception as e:print(f"❌ 失败 → {item['email']}:{e}")server.quit()print("全部完成")
代码的逻辑很直白:读表 → 循环 → 替换占位符 → 发信。BODY_TEMPLATE 里的 {name}、{code} 会被 Excel 中对应列的值替换掉。你唯一需要改的是配置区的几行,以及模板里占位符的名字要和 Excel 表头对得上。
几个容易踩的坑
授权码不是密码。 QQ、163、Gmail 都要求用“授权码”登录 SMTP,而不是你平时登录网页的密码。QQ 邮箱在“设置 → 账户”里开启 SMTP 服务后生成。把授权码写死在代码里可以,但如果你要把脚本发给别人或传到 Git,记得先删掉。
发送频率别太猛。 个人邮箱 SMTP 有日发送量限制,短时间内大量发信可能被临时封禁。几十封以内问题不大,上百封建议控制节奏(每封之间 time.sleep(1)),或者改用 SendGrid、阿里云邮件推送这类事务邮件服务。内部通知场景通常用个人邮箱就够。
Excel 里邮箱列要干净。 确保没有多余空格、没有合并单元格、没有空行夹在中间。脚本里用 if row[1] is None 跳过了空邮箱,但格式混乱的数据源仍然会让调试变得痛苦。发之前先打印前几条记录看一眼。
HTML 正文更好看的话,把 MIMEText 的第二个参数改成 "html"。正文里就可以写 <b>加粗</b>、<br> 换行、甚至简单的表格。邮件客户端对 HTML 的支持参差不齐,别用太花哨的 CSS,内联样式最稳。
和 Word 邮件合并怎么选
一句话判断:发附件用 Word 邮件合并,发正文用 Python。
工资条、录取通知书、合同这类需要正式版式的,Word 模板排版更方便,合并生成文档后再考虑怎么发。通知、提醒、简报这类正文本身就是全部内容的,Python 脚本直接发效率更高,也更容易塞进定时任务(配合 Windows 任务计划或 Linux crontab,每天早上一杯咖啡还没喝完,邮件已经发完了)。