本文将介绍一个功能完善的Python批量邮件发送脚本。该脚本能够从Excel文件读取联系人列表,并使用Jinja2模板引擎生成个性化的邮件内容。脚本内置了发送间隔控制与日志记录功能,能有效规避邮件服务器的发送限制,确保大批量邮件的稳定投递。
功能概述
该邮件发送器主要包含以下核心功能:
- 数据源:从Excel文件读取收件人姓名与邮箱地址。
- 模板渲染:支持使用Jinja2编写HTML富文本邮件模板,也支持简单的纯文本模板。
- 个性化:可根据每位收件人的信息动态填充邮件主题和内容。
- 稳定发送:可配置发送间隔与每批发送数量,防止被识别为垃圾邮件或触发发送限制。
- 完整日志:记录每次发送的成功与失败状态,便于后续核对与排查问题。
环境依赖
脚本依赖于以下三个Python库:
- yagmail: 一个简化SMTP邮件发送过程的库。
- pandas: 用于读取和操作Excel文件中的数据。
- jinja2: 强大的模板引擎,用于渲染HTML邮件内容。
你可以使用pip命令进行安装:
pip install yagmail pandas jinja2
核心配置详解
1. 邮箱服务器配置 (EmailConfig)
在此配置发件邮箱的账户信息与SMTP服务器参数。请注意,部分邮箱(如163、QQ)需使用授权码而非登录密码。
class EmailConfig:
"""邮箱配置"""
# 你的邮箱
user: str = 'your_email@163.com'
# 邮箱授权码(非登录密码)
password: str = 'your_authorization_code'
# 邮箱SMTP服务器
host: str = 'smtp.163.com'
# 端口(推荐使用587或465)
port: int = '587'
2. 发件人信息与主题 (SenderInfo)
此配置类定义了发件人信息,并负责生成个性化的邮件主题。
class SenderInfo:
"""发件人信息"""
name: str = '云栈社区'
position: str = '技术编辑'
company: str = '云栈科技'
email: str = 'your_email@163.com'
signature: str = '感谢阅读!'
def get_subject(self, client: dict):
"""生成邮件主题,client为从Excel读取的单条客户信息"""
return f"{self.name}致{client['name']}的问候邮件"
def to_json(self):
"""将发件人信息转换为字典,供模板使用"""
return {
'name': self.name,
'position': self.position,
'company': self.company,
'email': self.email,
'signature': self.signature
}
3. 发送行为控制 (SendSettings)
通过调整此配置,可以控制发送节奏,是避免被限流的关键。
class SendSettings:
"""发送设置"""
# 邮件间隔(秒),避免被限制
delay_between_emails: int = 2
# 测试模式(开启后,所有邮件将发往自己的邮箱)
test_mode: bool = True # 建议先开启测试,验证无误后再设为False
# 每批最多发送量(达到此数量后暂停60秒)
max_emails_per_batch: int = 20
脚本核心流程解析
1. 加载客户数据
使用pandas读取Excel文件。假设Excel格式如下:
| index |
name |
email |
| 1 |
张三 |
zhangsan@example.com |
| 2 |
李四 |
lisi@example.com |
def load_clients(self) -> list[dict]:
"""加载并校验客户数据"""
df = pd.read_excel(self.client_data_file)
client_list = df.to_dict(orient='records')
if not client_list:
raise ValueError("客户数据为空")
# 数据清洗与校验
for client in client_list:
if not client['email']:
raise ValueError(f"客户 {client.get('name')} 的邮箱为空")
if not client['name']:
raise ValueError(f"邮箱 {client.get('email')} 的姓名为空")
client['name'] = client['name'].strip()
client['email'] = client['email'].strip()
print(f"已加载 {len(client_list)} 个客户数据")
return client_list
这是典型的Python自动化处理场景,pandas在此高效地完成了数据的读取与结构化。
2. 加载与渲染邮件模板
脚本支持两种模板:Jinja2 HTML模板和简单文本模板。
- 文本模板 (
christmas.txt):使用{client_name}、{sender_company}等占位符。
尊敬的{client_name},您好!
我是{sender_company}的{sender_name}。
{sender_signature}
- Jinja2 HTML模板 (
template.html):功能更强大,支持条件、循环等逻辑。
<html><body>
<p>尊敬的{{ client.name }},您好!</p>
<p>我是{{ sender.company }}的{{ sender.name }}。</p>
<p><em>{{ sender.signature }}</em></p>
</body></html>
3. 发送邮件与日志记录
发送功能封装在send_to_client方法中,每封邮件发送无论成功失败都会记录详细的日志。
def send_to_client(self, client, content):
"""发送给单个客户"""
try:
subject = self.sender_info.get_subject(client=client)
to_email = self.email_config.user if self.send_settings.test_mode else client['email']
if self.send_settings.test_mode:
subject = f"[测试] {subject}"
self.yag.send(to=to_email, subject=subject, contents=content)
# 记录成功日志
self.sent_log.append({
'client': client['name'],
'email': client['email'],
'time': datetime.now().isoformat(),
'status': '成功'
})
print(f"✓已发送给 {client['name']} <{client['email']}>")
return True
except Exception as e:
# 记录失败日志
self.sent_log.append({
'client': client['name'],
'email': client['email'],
'time': datetime.now().isoformat(),
'status': f'失败: {str(e)}'
})
print(f"✗发送失败 {client['name']}: {e}")
return False
4. 主运行逻辑 (run方法)
run方法串联了整个发送流程,并集成了防限流策略。
def run(self):
"""主运行函数"""
print("开始发送个性化邮件...\n")
client_list = self.load_clients()
template, txt_template = self.load_template()
for i, client in enumerate(client_list, 1):
# 1. 生成个性化内容
html_content, text_content = self.personalize_content(client, template, txt_template)
# 2. 发送邮件
self.send_to_client(client, content)
# 3. 发送间隔延迟
if i < len(client_list):
time.sleep(self.send_settings.delay_between_emails)
# 4. 分批暂停(防限流关键)
if i % self.send_settings.max_emails_per_batch == 0:
print(f"\n已发送 {i} 封,暂停 60 秒...")
time.sleep(60)
self.save_log() # 保存所有日志到CSV文件
print(f"\n✅完成!")
如何使用脚本
- 准备数据:将收件人信息填入Excel文件(如
clients.xlsx)。
- 编写模板:创建纯文本模板(如
christmas.txt)或Jinja2 HTML模板。
- 修改配置:在
config.py中正确设置邮箱授权码、发件人信息等。
- 测试运行:将
SendSettings.test_mode设为True,运行脚本检查邮件内容与格式。
- 正式发送:确认无误后,将
test_mode设为False,再次运行脚本开始正式批量发送。
运行入口示例:
if __name__ == "__main__":
client_data_file = './clients/clients.xlsx'
txt_template_file = './templates/christmas.txt' # 或指定html模板文件
sender = EmailSender(client_data_file=client_data_file,
txt_template_file=txt_template_file)
sender.run()
总结
本脚本整合了邮件发送、数据处理与模板渲染等多个环节,通过模块化的配置和健壮的发送策略,提供了一个可用于实际生产环境的批量邮件发送解决方案。开发者可根据自身需求,轻松扩展附件支持、更复杂的模板逻辑或其他的邮件头部信息。