← 返回列表
自动定时发送邮件
通过 SMTP 协议配置邮件服务器,实现定时向指定收件人列表发送带格式的通知邮件。常用于系统报警或每日工作汇报的自动化流程。
快速预览:
import smtplib
from email.mime.text import MIMEText
msg = MIMEText("这是自动发送的测试邮件内容")
msg['Subject'] = "每日系统报告"
with smtplib.SMTP('smtp.server.com', 587) as server:
server.login('user@example.com', 'password')
server.send_message(msg)
应用场景与价值
自动发送邮件是办公自动化的重要组成部分,在多种场景下极具价值:
- 系统监控报警: 服务器异常、磁盘空间不足等问题的自动通知
- 定期报表: 每日销售数据、网站流量统计的自动汇总发送
- 任务提醒: 项目截止日期、会议通知的自动提醒
- 数据备份通知: 备份任务完成状态的自动报告
- 批量通知: 向客户群发促销信息、系统维护通知
- 自动回复: 订单确认、注册验证等自动邮件
通过 Python 实现邮件自动化,可以节省大量重复性工作时间,确保重要信息及时传达,提升工作效率和响应速度。
基础知识
SMTP 协议
SMTP(Simple Mail Transfer Protocol)是发送邮件的标准协议。主要概念:
- SMTP 服务器: 负责发送邮件的服务器(如 Gmail、QQ 邮箱的 SMTP 服务器)
- 端口号:
- 25: 传统 SMTP 端口(多数被封禁)
- 587: TLS 加密端口(推荐)
- 465: SSL 加密端口
常见邮箱 SMTP 配置
| 邮箱服务 | SMTP 服务器 | 端口 | 说明 |
|---|---|---|---|
| Gmail | smtp.gmail.com | 587 | 需开启"不够安全的应用访问权限" |
| QQ 邮箱 | smtp.qq.com | 587 | 需开启 SMTP 服务并使用授权码 |
| 163 邮箱 | smtp.163.com | 465 | 需开启 SMTP 服务并使用授权码 |
| Outlook | smtp.office365.com | 587 | 需使用应用密码 |
email 模块
Python 的 email 模块用于构建邮件内容:
from email.mime.text import MIMEText # 纯文本邮件
from email.mime.multipart import MIMEMultipart # 复杂邮件(带附件)
from email.mime.image import MIMEImage # 图片附件
from email.mime.base import MIMEBase # 通用附件
代码详解
让我们深入理解邮件发送的每一步:
import smtplib
from email.mime.text import MIMEText
导入必需的模块。smtplib 处理 SMTP 连接,MIMEText 用于创建邮件内容。
msg = MIMEText("这是自动发送的测试邮件内容")
创建邮件对象,参数是邮件正文内容。默认为纯文本格式。
msg['Subject'] = "每日系统报告"
设置邮件主题。这是收件人在收件箱中看到的标题。
with smtplib.SMTP('smtp.server.com', 587) as server:
创建 SMTP 连接。使用 with 语句确保连接自动关闭。587 是 TLS 加密端口。
server.login('user@example.com', 'password')
使用邮箱账号和密码(或授权码)登录 SMTP 服务器进行身份验证。
server.send_message(msg)
发送邮件。send_message() 方法会自动从 msg 对象中提取发件人、收件人等信息。
完整代码
基础版本:发送简单文本邮件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def send_email(subject, body, to_email, from_email, password, smtp_server, smtp_port):
"""
发送简单文本邮件
参数:
subject: 邮件主题
body: 邮件正文
to_email: 收件人邮箱
from_email: 发件人邮箱
password: 发件人邮箱密码或授权码
smtp_server: SMTP 服务器地址
smtp_port: SMTP 端口
"""
try:
# 创建邮件对象
msg = MIMEMultipart()
msg['From'] = from_email
msg['To'] = to_email
msg['Subject'] = subject
# 添加邮件正文
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# 连接 SMTP 服务器
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls() # 启用 TLS 加密
server.login(from_email, password)
server.send_message(msg)
print(f"邮件发送成功! 收件人: {to_email}")
return True
except Exception as e:
print(f"邮件发送失败: {e}")
return False
# 使用示例
if __name__ == "__main__":
# 配置信息
SMTP_SERVER = 'smtp.qq.com'
SMTP_PORT = 587
FROM_EMAIL = 'your_email@qq.com'
PASSWORD = 'your_authorization_code' # QQ 邮箱授权码
# 发送邮件
send_email(
subject="每日系统报告",
body="系统运行正常,今日无异常。",
to_email="recipient@example.com",
from_email=FROM_EMAIL,
password=PASSWORD,
smtp_server=SMTP_SERVER,
smtp_port=SMTP_PORT
)
进阶版本:发送 HTML 格式邮件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime
def send_html_email(subject, html_content, to_emails, from_email, password, smtp_server, smtp_port):
"""
发送 HTML 格式邮件,支持多个收件人
参数:
to_emails: 收件人列表,如 ['user1@example.com', 'user2@example.com']
"""
try:
msg = MIMEMultipart('alternative')
msg['From'] = from_email
msg['To'] = ', '.join(to_emails)
msg['Subject'] = subject
# 添加纯文本版本(备用)
text_part = MIMEText("此邮件需要 HTML 支持查看", 'plain', 'utf-8')
msg.attach(text_part)
# 添加 HTML 版本
html_part = MIMEText(html_content, 'html', 'utf-8')
msg.attach(html_part)
# 发送邮件
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(from_email, password)
server.send_message(msg)
print(f"HTML 邮件发送成功! 收件人: {', '.join(to_emails)}")
return True
except Exception as e:
print(f"发送失败: {e}")
return False
# HTML 邮件模板
html_template = """
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; }}
.header {{ background-color: #4CAF50; color: white; padding: 20px; }}
.content {{ padding: 20px; }}
.footer {{ background-color: #f1f1f1; padding: 10px; text-align: center; }}
table {{ border-collapse: collapse; width: 100%; }}
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
th {{ background-color: #4CAF50; color: white; }}
</style>
</head>
<body>
<div class="header">
<h1>{title}</h1>
</div>
<div class="content">
<p>报告日期: {date}</p>
<h2>数据汇总</h2>
<table>
<tr>
<th>指标</th>
<th>数值</th>
</tr>
<tr>
<td>总销售额</td>
<td>${total_sales}</td>
</tr>
<tr>
<td>订单数量</td>
<td>{order_count}</td>
</tr>
</table>
</div>
<div class="footer">
<p>此邮件由系统自动生成</p>
</div>
</body>
</html>
"""
# 使用示例
html_content = html_template.format(
title="每日销售报告",
date=datetime.now().strftime('%Y-%m-%d'),
total_sales="25,340.00",
order_count=156
)
send_html_email(
subject="每日销售报告 - 2026-01-09",
html_content=html_content,
to_emails=['boss@company.com', 'manager@company.com'],
from_email='system@company.com',
password='your_password',
smtp_server='smtp.company.com',
smtp_port=587
)
高级版本:发送带附件的邮件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import os
def send_email_with_attachment(subject, body, to_email, from_email, password,
smtp_server, smtp_port, attachment_path):
"""
发送带附件的邮件
参数:
attachment_path: 附件文件路径
"""
try:
msg = MIMEMultipart()
msg['From'] = from_email
msg['To'] = to_email
msg['Subject'] = subject
# 添加邮件正文
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# 添加附件
if os.path.exists(attachment_path):
with open(attachment_path, 'rb') as f:
part = MIMEBase('application', 'octet-stream')
part.set_payload(f.read())
encoders.encode_base64(part)
filename = os.path.basename(attachment_path)
part.add_header('Content-Disposition', f'attachment; filename= {filename}')
msg.attach(part)
else:
print(f"警告: 附件 {attachment_path} 不存在")
# 发送邮件
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(from_email, password)
server.send_message(msg)
print(f"带附件的邮件发送成功!")
return True
except Exception as e:
print(f"发送失败: {e}")
return False
# 使用示例
send_email_with_attachment(
subject="销售报表",
body="请查收附件中的本月销售数据报表。",
to_email="manager@company.com",
from_email="system@company.com",
password="your_password",
smtp_server="smtp.company.com",
smtp_port=587,
attachment_path="./sales_report.xlsx"
)
定时发送版本
import schedule
import time
from datetime import datetime
def send_daily_report():
"""每日报告任务"""
print(f"[{datetime.now()}] 开始发送每日报告...")
# 这里调用前面定义的发送邮件函数
send_email(
subject=f"每日报告 - {datetime.now().strftime('%Y-%m-%d')}",
body="今日系统运行正常。",
to_email="admin@company.com",
from_email="system@company.com",
password="your_password",
smtp_server="smtp.company.com",
smtp_port=587
)
# 配置定时任务
schedule.every().day.at("09:00").do(send_daily_report) # 每天 9:00 发送
schedule.every().monday.at("10:00").do(send_daily_report) # 每周一 10:00 发送
schedule.every().hour.do(send_daily_report) # 每小时发送
print("定时任务已启动...")
while True:
schedule.run_pending()
time.sleep(60) # 每分钟检查一次
运行示例
安装依赖
uv add schedule
获取 QQ 邮箱授权码
- 登录 QQ 邮箱网页版
- 设置 -> 账户 -> POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务
- 开启 SMTP 服务
- 生成授权码(这个授权码用于代替邮箱密码)
执行发送
uv run main.py
预期输出
邮件发送成功! 收件人: recipient@example.com
扩展思路
1. 批量发送邮件
def send_bulk_emails(recipients, subject, body, from_email, password, smtp_server, smtp_port):
"""批量发送邮件,为每个收件人单独发送"""
success_count = 0
for recipient in recipients:
if send_email(subject, body, recipient, from_email, password, smtp_server, smtp_port):
success_count += 1
time.sleep(1) # 延迟防止被识别为垃圾邮件
print(f"批量发送完成: {success_count}/{len(recipients)} 成功")
2. 邮件模板系统
from string import Template
# 定义模板
template = Template("""
尊敬的 $name,
您好! 这是一封自动发送的邮件。
订单号: $order_id
总金额: $amount
感谢您的支持!
""")
# 使用模板
for user in users:
body = template.substitute(
name=user['name'],
order_id=user['order_id'],
amount=user['amount']
)
send_email(subject="订单确认", body=body, to_email=user['email'], ...)
3. 错误重试机制
def send_email_with_retry(max_retries=3, **kwargs):
"""带重试的邮件发送"""
for attempt in range(max_retries):
try:
if send_email(**kwargs):
return True
except Exception as e:
print(f"第 {attempt + 1} 次尝试失败: {e}")
if attempt < max_retries - 1:
time.sleep(2 ** attempt) # 指数退避
return False
4. 发送日志记录
import logging
logging.basicConfig(
filename='email_log.txt',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def send_email_with_log(**kwargs):
"""带日志的邮件发送"""
try:
result = send_email(**kwargs)
logging.info(f"邮件发送成功: {kwargs['to_email']}")
return result
except Exception as e:
logging.error(f"邮件发送失败: {kwargs['to_email']} - {e}")
return False
5. 使用配置文件
import json
# config.json
{
"smtp_server": "smtp.qq.com",
"smtp_port": 587,
"from_email": "sender@qq.com",
"password": "authorization_code"
}
# 读取配置
with open('config.json', 'r') as f:
config = json.load(f)
send_email(**config, subject="Test", body="Test", to_email="user@example.com")
6. 邮件队列系统
from queue import Queue
from threading import Thread
email_queue = Queue()
def email_worker():
"""邮件发送工作线程"""
while True:
email_data = email_queue.get()
if email_data is None:
break
send_email(**email_data)
email_queue.task_done()
# 启动工作线程
worker = Thread(target=email_worker)
worker.start()
# 添加邮件到队列
email_queue.put({
'subject': 'Test',
'body': 'Test',
'to_email': 'user@example.com',
# ... 其他参数
})
7. 集成数据分析结果
import pandas as pd
# 从 CSV 读取数据并生成报告
df = pd.read_csv('sales_data.csv')
summary = f"""
每日销售报告
总销售额: ${df['amount'].sum():,.2f}
订单数量: {len(df)}
平均订单额: ${df['amount'].mean():,.2f}
"""
send_email(subject="每日销售报告", body=summary, ...)
注意事项
安全性
- 不要硬编码密码: 使用环境变量或配置文件存储敏感信息
- 使用授权码: 许多邮箱服务要求使用专门的授权码而非账户密码
- 加密连接: 始终使用 TLS/SSL 加密(
starttls())
反垃圾邮件
- 控制发送频率: 避免短时间内大量发送
- 验证收件人: 确保邮箱地址有效
- 提供退订选项: 批量邮件应提供退订链接
- 遵守法律法规: 遵守 CAN-SPAM 法案和 GDPR 等规定
最佳实践
- 异常处理: 妥善处理网络错误、认证失败等异常
- 日志记录: 记录发送状态,便于排查问题
- 测试环境: 先在测试环境验证,再部署到生产环境
- 邮件内容: 确保内容专业、格式正确、无拼写错误
通过掌握自动发送邮件技术,你可以构建强大的通知系统、报表系统和自动化工作流,显著提升工作效率和信息传递的及时性。