🐍 Python Day82:邮件自动化 — smtplib + email
🕐 预计用时:3-4 小时 | 🎯 目标:掌握 SMTP 发送邮件、HTML 邮件、附件和批量发送
📖 今日目录
1. 邮件协议简介
# 邮件发送流程:
# 你的电脑 → SMTP 服务器 → 收件人的邮件服务器 → 收件人
# 两个核心库:
# smtplib → 连接 SMTP 服务器、登录、发送
# email → 构造邮件内容(标题、正文、附件)
# 常用 SMTP 服务器:
# QQ 邮箱: smtp.qq.com:465 (SSL) 或 :587 (TLS)
# 163 邮箱: smtp.163.com:465 (SSL)
# Gmail: smtp.gmail.com:587 (TLS)
# Outlook: smtp.office365.com:587 (TLS)
# 企业邮箱: 各企业自定义
# QQ 邮箱授权码获取:
# 登录 QQ 邮箱 → 设置 → 账户 → POP3/SMTP 服务 → 开启 → 生成授权码
# ⚠️ 授权码 ≠ QQ 密码,是专门用于第三方登录的
2. 发送纯文本邮件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
# === 基础发送 ===
def send_simple_email(sender, password, receiver, subject, body):
"""发送纯文本邮件"""
# 构造邮件
msg = MIMEText(body, 'plain', 'utf-8')
msg['From'] = sender
msg['To'] = receiver
msg['Subject'] = Header(subject, 'utf-8')
# 连接 SMTP 服务器并发送
server = None
try:
# SSL 方式(QQ 邮箱、163 邮箱)
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender, password)
server.sendmail(sender, [receiver], msg.as_string())
print(f'✅ 邮件发送成功!收件人: {receiver}')
except Exception as e:
print(f'❌ 发送失败: {e}')
finally:
if server:
server.quit()
# 使用
# send_simple_email(
# sender='your@qq.com',
# password='your_auth_code', # 授权码,不是 QQ 密码!
# receiver='friend@example.com',
# subject='测试邮件',
# body='这是用 Python 自动发送的邮件!'
# )
# === TLS 方式(Gmail、Outlook)===
def send_tls_email(sender, password, receiver, subject, body):
"""TLS 方式发送"""
msg = MIMEText(body, 'plain', 'utf-8')
msg['From'] = sender
msg['To'] = receiver
msg['Subject'] = Header(subject, 'utf-8')
server = None
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo() # 问候
server.starttls() # 启用 TLS 加密
server.login(sender, password)
server.sendmail(sender, [receiver], msg.as_string())
print('✅ Gmail 发送成功!')
except Exception as e:
print(f'❌ 发送失败: {e}')
finally:
if server:
server.quit()
3. 发送 HTML 邮件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
def send_html_email(sender, password, receiver, subject, html_body):
"""发送 HTML 邮件(支持富文本格式)"""
# MIMEMultipart 支持多种内容(正文 + 附件)
msg = MIMEMultipart('alternative')
msg['From'] = sender
msg['To'] = receiver
msg['Subject'] = Header(subject, 'utf-8')
# 添加 HTML 正文
html_part = MIMEText(html_body, 'html', 'utf-8')
msg.attach(html_part)
server = None
try:
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender, password)
server.sendmail(sender, [receiver], msg.as_string())
print('✅ HTML 邮件发送成功!')
except Exception as e:
print(f'❌ 发送失败: {e}')
finally:
if server:
server.quit()
# === HTML 邮件模板 ===
html_template = '''
📊 月度销售报告
报告日期:2026年5月
一、销售概览
本月总销售额:¥185,000
环比增长:+12.5%
二、品类排名
三、下一步计划
本报告由 Python 自动生成 | 如有问题请联系 data@company.com
'''
# 使用
# send_html_email(
# sender='your@qq.com',
# password='your_auth_code',
# receiver='boss@company.com',
# subject='5月销售报告',
# html_body=html_template
# )
💡 HTML 邮件注意事项:
• 邮件客户端的 CSS 支持很有限——不能用 flexbox、grid、外部 CSS
• 样式必须用内联 style 或 <style> 块
• 表格布局是最安全的方式(所有客户端都支持)
• 图片不要用外部链接(会被屏蔽),用内嵌方式
• 字体只用系统字体(微软雅黑、Arial、宋体)
4. 发送带附件邮件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email import encoders
from email.header import Header
import os
def send_email_with_attachment(sender, password, receiver, subject, body, attachments=None):
"""发送带附件的邮件"""
# mixed 类型支持正文 + 附件
msg = MIMEMultipart('mixed')
msg['From'] = sender
msg['To'] = receiver
msg['Subject'] = Header(subject, 'utf-8')
# 添加正文(HTML)
html_part = MIMEText(body, 'html', 'utf-8')
msg.attach(html_part)
# 添加附件
if attachments:
for filepath in attachments:
if not os.path.exists(filepath):
print(f'⚠️ 文件不存在: {filepath}')
continue
filename = os.path.basename(filepath)
with open(filepath, 'rb') as f:
# 通用附件
attachment = MIMEBase('application', 'octet-stream')
attachment.set_payload(f.read())
encoders.encode_base64(attachment)
attachment.add_header(
'Content-Disposition',
'attachment',
filename=('utf-8', '', filename) # 支持中文文件名
)
msg.attach(attachment)
print(f'📎 已添加附件: {filename}')
# 发送
server = None
try:
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender, password)
server.sendmail(sender, [receiver], msg.as_string())
print(f'✅ 带附件的邮件发送成功!')
except Exception as e:
print(f'❌ 发送失败: {e}')
finally:
if server:
server.quit()
# 使用
# send_email_with_attachment(
# sender='your@qq.com',
# password='your_auth_code',
# receiver='boss@company.com',
# subject='月度报表',
# body='附件是本月销售报表
请查收。
',
# attachments=[
# '销售报表.xlsx',
# '数据分析.pdf',
# 'chart.png',
# ]
# )
5. 发送图片内嵌邮件
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.header import Header
def send_email_with_inline_image(sender, password, receiver, subject, html_body, image_paths):
"""发送内嵌图片的 HTML 邮件"""
msg = MIMEMultipart('related')
msg['From'] = sender
msg['To'] = receiver
msg['Subject'] = Header(subject, 'utf-8')
# HTML 正文(用 cid: 引用内嵌图片)
html_part = MIMEText(html_body, 'html', 'utf-8')
msg.attach(html_part)
# 内嵌图片
for i, img_path in enumerate(image_paths):
with open(img_path, 'rb') as f:
img = MIMEImage(f.read())
img.add_header('Content-ID', f'') # 设置 Content-ID
img.add_header('Content-Disposition', 'inline', filename=img_path)
msg.attach(img)
# 发送
server = None
try:
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender, password)
server.sendmail(sender, [receiver], msg.as_string())
print('✅ 内嵌图片邮件发送成功!')
except Exception as e:
print(f'❌ 发送失败: {e}')
finally:
if server:
server.quit()
# HTML 中用 cid: 引用图片
html_with_images = '''
📊 数据可视化报告
以下是本月的销售趋势图:
以下是品类占比图:
'''
# 使用
# send_email_with_inline_image(
# sender='your@qq.com',
# password='your_auth_code',
# receiver='team@company.com',
# subject='数据可视化报告',
# html_body=html_with_images,
# image_paths=['trend_chart.png', 'pie_chart.png']
# )
6. 批量发送
import smtplib
import pandas as pd
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
import time
def send_bulk_emails(sender, password, recipients_file, subject_template, body_template):
"""批量发送个性化邮件"""
# 读取收件人列表
df = pd.read_excel(recipients_file)
# 假设 Excel 有:姓名、邮箱、部门、绩效 等列
# 连接 SMTP 服务器(只连接一次,复用连接)
server = None
success = 0
failed = 0
try:
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender, password)
for _, row in df.iterrows():
try:
# 个性化内容(替换模板变量)
name = row['姓名']
email_addr = row['邮箱']
dept = row['部门']
score = row['绩效']
subject = subject_template.format(name=name)
body = body_template.format(
name=name,
department=dept,
score=score,
)
# 构造邮件
msg = MIMEText(body, 'html', 'utf-8')
msg['From'] = sender
msg['To'] = email_addr
msg['Subject'] = Header(subject, 'utf-8')
# 发送
server.sendmail(sender, [email_addr], msg.as_string())
print(f' ✅ {name} ({email_addr})')
success += 1
# 间隔 2 秒,避免被服务器限流
time.sleep(2)
except Exception as e:
print(f' ❌ {name}: {e}')
failed += 1
finally:
if server:
server.quit()
print(f'\n📊 发送完成: 成功 {success}, 失败 {failed}')
# === 模板 ===
subject_template = '{name},您的月度绩效报告'
body_template = '''
📊 月度绩效报告
{name} 您好,
以下是您本月的绩效概况:
请查看附件了解详细数据。
如有疑问,请联系 HR。
祝好!
'''
# 使用
# send_bulk_emails(
# sender='your@qq.com',
# password='your_auth_code',
# recipients_file='员工列表.xlsx',
# subject_template=subject_template,
# body_template=body_template
# )
💡 批量发送注意事项:
• 间隔:每封邮件间隔 2-3 秒,避免被 SMTP 服务器限流
• 日志:记录每封邮件的发送状态(成功/失败)
• 断点续发:如果中途失败,记录已发送的,下次跳过
• 频率限制:QQ 邮箱每天最多发送约 500 封,163 约 200 封
• 退订:商业邮件必须提供退订链接
7. 实战:自动发送日报
"""
实战场景:每天下午 6 点自动收集今日工作数据,生成 HTML 报告,发送给团队
"""
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from email.header import Header
from datetime import datetime
import pandas as pd
def generate_daily_report(data_file):
"""生成日报 HTML"""
df = pd.read_excel(data_file)
today = datetime.now().strftime('%Y-%m-%d')
# 统计数据
total_orders = len(df)
total_sales = df['金额'].sum()
avg_order = df['金额'].mean()
top_product = df.groupby('商品')['金额'].sum().idxmax()
html = f'''
📊 每日销售简报
{today}
📈 今日概览
{total_orders}
订单数 |
¥{total_sales:,.0f}
总销售额 |
¥{avg_order:,.0f}
客单价 |
🏆 Top 商品
{top_product} 是今日销售额最高的商品
📋 订单明细
'''
for _, row in df.head(20).iterrows():
html += f'''
'''
html += '''
本报告由 Python 自动生成 | 下一报告时间:明日 18:00
'''
return html
def send_daily_report(sender, password, recipients, data_file):
"""发送日报"""
html = generate_daily_report(data_file)
today = datetime.now().strftime('%Y-%m-%d')
msg = MIMEMultipart('mixed')
msg['From'] = sender
msg['To'] = ', '.join(recipients)
msg['Subject'] = Header(f'📊 每日销售简报 {today}', 'utf-8')
html_part = MIMEText(html, 'html', 'utf-8')
msg.attach(html_part)
try:
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender, password)
server.sendmail(sender, recipients, msg.as_string())
server.quit()
print(f'✅ 日报发送成功!收件人: {len(recipients)} 人')
except Exception as e:
print(f'❌ 发送失败: {e}')
# 使用
# send_daily_report(
# sender='your@qq.com',
# password='your_auth_code',
# recipients=['team1@company.com', 'team2@company.com'],
# data_file='今日销售数据.xlsx'
# )
8. 今日练习
🏋️ 练习 1:发送测试邮件
# 用你的邮箱给自己发一封测试邮件
# 包含:HTML 正文 + 一个附件
# 注意:需要先开启 SMTP 服务并获取授权码
🏋️ 练习 2:批量通知
# 创建一个 Excel 收件人列表(姓名、邮箱、部门)
# 给每个人发送个性化的 HTML 邮件
# 每封邮件包含他们的姓名和部门信息
🏋️ 练习 3:报表邮件
# 用 openpyxl 生成一份 Excel 报表
# 然后用邮件自动发送给指定收件人
# 邮件正文包含报表摘要(HTML 表格)
9. 今日小结
| |
|---|
| MIMEText + SMTP_SSL / starttls |
| MIMEMultipart('alternative') + HTML 模板 |
| MIMEBase + encode_base64 + Content-Disposition |
| MIMEImage + Content-ID + cid: 引用 |
| |
| |
🚀 明日预告:Day 83 — 定时任务
schedule 库、APScheduler、系统 crontab——让 Python 脚本按时自动运行。配合邮件和 Excel,实现全自动化的办公流水线!