告别手动发周报:Python自动发邮件完整方案
周一早上的"至暗时刻"
以前每周一一大早的"标准动作"是:打开邮箱、打开Excel、跑SQL、把数据贴到Excel里、做几张图、调格式、写分析、转PDF、写邮件内容、挨个加抄送......
这一套下来,保底两小时,遇到数据异常特殊情况的,3-5个小时就搭进去了。

更崩溃的是,每周一都是一样的套路,连改都没法改,纯硬活
现在我用AI辅助写Python把整个流程写成脚本,每周一早8点,报表自己生成、自己出图、自己整理邮件,等我到公司,点上杯瑞幸,然后过一遍数据和邮件内容就行了。
整体工作流程没有想象的复杂,今天给大家分享一下整体方案
整体方案:四个模块串成一条线
整条自动化流程,差不多四个模块:
取数:pandas读SQL/Excel/中间表,输出DataFrame
出图:matplotlib生成销售趋势、Top门店、品类分布三张图
发邮件:yagmail或smtplib发HTML邮件,附件带图片和Excel
定时:schedule库每周一8点自动触发,全程零人工

第一步:取数(pandas读数据)
我用pandas从SQL直接拉数据,示例代码:
import pandas as pd
from sqlalchemy import create_engine
# 引擎配置,按自己公司改
engine = create_engine('mysql+pymysql://user:pwd@host:3306/db')
# 每周一跑上周数据
sql = """
SELECT
store_name,
SUM(sales) AS total_sales,
COUNT(DISTINCT member_id) AS member_cnt
FROM daily_sales
WHERE sale_date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
AND DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY)
GROUP BY store_name
ORDER BY total_sales DESC
"""
df = pd.read_sql(sql, engine)
注意一个小坑:任务最好放在凌晨跑,用`DATE_SUB`比写死日期省心,跨月跨年都不会出错。
第二步:出图(matplotlib三张图)
用matplotlib生成三张关键图,存到本地临时目录:
import matplotlib.pyplot as plt
import os
# 中文字体配置(Mac/Win不一样,按你电脑改)
plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Hiragino Sans GB']
plt.rcParams['axes.unicode_minus'] = False
os.makedirs('/tmp/report_img', exist_ok=True)
# 图1:门店销售Top10
top10 = df.head(10)
plt.figure(figsize=(10, 6))
plt.barh(top10['store_name'], top10['total_sales'], color='#E74C3C')
plt.title('门店销售Top10', fontsize=16)
plt.gca().invert_yaxis()
plt.tight_layout()
plt.savefig('/tmp/report_img/top10.png', dpi=120)
plt.close()
剩下两张图(趋势图、品类分布)写法一样,就是改下数据和颜色。这块代码是死的,跑一次就能复用。
第三步:发邮件(yagmail最省事)
我对比过smtplib和yagmail。yagmail就够用,比smtplib少写一半代码,附件、内嵌图片、HTML正文都一行搞定。
import yagmail
# 一次性配好(密码用QQ邮箱授权码,不是登录密码)
yag = yagmail.SMTP(
user='your@qq.com',
password='你的QQ邮箱授权码', # QQ邮箱设置里生成
host='smtp.qq.com'
)
# HTML正文
html = f"""
<h2>上周销售周报(自动生成)</h2>
<p>本周总销售额:<b>{df['total_sales'].sum():,.0f}元</b></p>
<p>活跃会员数:<b>{df['member_cnt'].sum():,}人</b></p>
<p>详见附件和图片。</p>
"""
# 发送(收件人+抄送+附件+正文,一行搞定)
yag.send(
to=['leader@company.com'],
cc=['me@company.com'],
subject='【周报】上周销售数据',
contents=[yagmail.inline('/tmp/report_img/top10.png'), html],
attachments=['/tmp/report_img/top10.png', '/tmp/report_img/趋势.png']
)
几个容易踩的坑:
• 授权码不是密码:QQ/163/企业邮箱都要去后台单独开授权码
• 图片内嵌用yagmail.inline():不然图片在收件人那边是裂的
• 抄送cc和密送bcc要分清:cc所有人可见,bcc不可见
第四步:定时(schedule最简单)
定时方案我试过三个,schedule、cron、APScheduler——个人推荐schedule,10行代码搞定:
import schedule
import time
def job():
# 把前三步打包成一个函数
run_full_pipeline()
# 每周一早8点跑
schedule.every().monday.at('08:00').do(job)
while True:
schedule.run_pending()
time.sleep(60) # 每分钟检查一次
这个脚本扔到服务器后台跑(用`nohup python script.py &`),周一早上你还没到工位,报表已经躺在领导邮箱里了。
我自己用的时候,还多了两块活儿
基础版能节约2小时。我自己用的版本还多了两块:
多门店报表自动拆分,用`groupby('store_name')`拆分数据,每个店长只看自己店的数据,邮件正文用f-string动态插入店长名字,加个`for`循环解决。
写在最后:数据人最该先自动化什么
个人认为,最该自动化的不是分析环节,是"交付环节"。
分析再花哨,最后一公里是"让该看的人看到"。Excel再漂亮,没有触达到目标用户手上就是零。Python这玩意儿最大的价值不是帮你算数据,是把"算完数据→送到决策者手里"这条链路打通。
额外再插一嘴,现在各种AI工具已经基本成熟了,推荐使用workbuddy创建自动任务,可以帮助0编程基础的数据员,从头梳理各种卡点,它会帮你调试代码搭建自动任务,不过需要警惕的是AI出的数据结果一定要自己复核一遍,我被坑了好多次了,唉......