影刀RPA进阶教程:Python自动生成数据可视化图表与飞书日报

痛点切入

我之前带的运营团队有个固定流程:每天早上9点,从各个平台导出前一天数据,手动粘到Excel里,再做成柱状图、折线图,截图发飞书群。一套流程下来至少40分钟。关键是图表长得还不统一——张三用蓝柱子、李四用红柱子,配色随心所欲,领导看起来很不专业。

后来我用影刀RPA搭了一套自动化方案:每天凌晨自动采集数据 → Python自动生成图表 → 飞书机器人推送日报。按这个方案跑了一年多,出错率为零,还省出了一个实习生的成本。这篇文章就把这个方案完整拆解给你。
在这里插入图片描述


核心方法

场景:电商多平台销售日报自动生成

某运营团队管理天猫、京东、拼多多三个平台,每天需要:

  1. 自动采集三个平台的昨日销售数据
  2. 生成销售额对比柱状图、品类占比饼图、7日趋势折线图
  3. 三张图自动拼成一张日报长图
  4. 通过飞书机器人推送到指定群聊

分步操作

在这里插入图片描述

第一步:数据采集与汇总

在影刀中搭建采集流程:

  • 「打开网页」→ 登录各平台后台
  • 「数据抓取」→ 抓取销售额、订单量、客单价、退货率
  • 「Excel写入」→ 写入统一格式的汇总表

汇总表结构(D:\数据\日报数据源.xlsx):

日期平台销售额订单量客单价退货率
2026-06-30天猫85000120070.83.2%
2026-06-30京东6200085072.92.1%
在这里插入图片描述

| 2026-06-30 | 拼多多 | 43000 | 950 | 45.3 | 4.5% |

第二步:Python生成图表

拖入「Python脚本」节点,核心代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
影刀RPA — 自动生成数据可视化日报图表
功能:读取Excel数据源 → 生成柱状图/饼图/趋势图 → 合成日报长图
作者:林焱
"""

import pandas as pd
import matplotlib
matplotlib.use('Agg')  # 无GUI后端,影刀环境下必须设置
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from matplotlib import font_manager
import os
from datetime import datetime, timedelta

# ========== 字体配置 ==========
# Windows环境指定中文字体,否则图表中文会显示为方框
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
# ===============================


def create_daily_report():
    """
    主函数:生成完整日报图表
    数据来源:影刀前置流程写入的Excel
    """
    
    # ========== 1. 读取数据 ==========
    data_file = r"D:\数据\日报数据源.xlsx"
    df = pd.read_excel(data_file, sheet_name="汇总")
    
    # 获取昨日日期用于图表标题
    yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
    
    # 输出目录
    output_dir = r"D:\数据\日报图表"
    os.makedirs(output_dir, exist_ok=True)
    
    # ========== 2. 设置统一的视觉风格 ==========
    # 公司配色方案(你可以改成自己公司的VI色)
    COLORS = {
        '天猫': '#FF6B6B',   # 珊瑚红
        '京东': '#4ECDC4',   # 青绿色
        '拼多多': '#FFE66D',  # 明黄色
        '正面': '#2ECC71',
        '负面': '#E74C3C',
        '中性': '#3498DB'
    }
    
    plt.style.use('seaborn-v0_8-whitegrid')
    
    # ========== 3. 生成图表1:平台销售额对比柱状图 ==========
    fig1, ax1 = plt.subplots(figsize=(10, 6))
    
    today_data = df[df['日期'] == yesterday]
    platforms = today_data['平台'].tolist()
    sales = today_data['销售额'].tolist()
    
    bars = ax1.bar(
        platforms, 
        sales,
        color=[COLORS.get(p, '#95A5A6') for p in platforms],
        width=0.5,
        edgecolor='white',
        linewidth=0.8
    )
    
    # 在柱子上标注数值
    for bar, val in zip(bars, sales):
        ax1.text(
            bar.get_x() + bar.get_width()/2,
            bar.get_height() + 1000,
            f'{val/10000:.1f}万',
            ha='center', va='bottom',
            fontsize=12, fontweight='bold'
        )
    
    ax1.set_title(f'{yesterday} 各平台销售额对比', fontsize=18, fontweight='bold', pad=15)
    ax1.set_ylabel('销售额(元)', fontsize=12)
    ax1.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f'{x/10000:.0f}万'))
    ax1.spines['top'].set_visible(False)
    ax1.spines['right'].set_visible(False)
    
    chart1_path = os.path.join(output_dir, f'chart1_sales_{yesterday}.png')
    fig1.savefig(chart1_path, dpi=150, bbox_inches='tight', facecolor='white')
    plt.close(fig1)
    print(f"[√] 柱状图已保存: {chart1_path}")
    
    # ========== 4. 生成图表2:品类销售额占比饼图 ==========
    # 假设品类数据在另一个sheet
    category_df = pd.read_excel(data_file, sheet_name="品类明细")
    cat_today = category_df[category_df['日期'] == yesterday]
    
    fig2, ax2 = plt.subplots(figsize=(8, 8))
    
    labels = cat_today['品类'].tolist()
    values = cat_today['销售额'].tolist()
    explode = [0.03] * len(labels)  # 所有扇区轻微分离
    
    wedges, texts, autotexts = ax2.pie(
        values,
        labels=labels,
        autopct='%1.1f%%',
        startangle=90,
        explode=explode,
        colors=['#FF6B6B', '#4ECDC4', '#FFE66D', '#95A5A6', '#3498DB', '#9B59B6'],
        textprops={'fontsize': 11}
    )
    # 百分比文字加粗
    for at in autotexts:
        at.set_fontweight('bold')
    
    ax2.set_title(f'{yesterday} 品类销售额占比', fontsize=18, fontweight='bold', pad=20)
    
    chart2_path = os.path.join(output_dir, f'chart2_category_{yesterday}.png')
    fig2.savefig(chart2_path, dpi=150, bbox_inches='tight', facecolor='white')
    plt.close(fig2)
    print(f"[√] 饼图已保存: {chart2_path}")
    
    # ========== 5. 生成图表3:7日销售额趋势折线图 ==========
    last_7_days = []
    for i in range(6, -1, -1):
        last_7_days.append((datetime.now() - timedelta(days=i+1)).strftime("%Y-%m-%d"))
    
    trend_df = df[df['日期'].isin(last_7_days)]
    
    fig3, ax3 = plt.subplots(figsize=(12, 5))
    
    for platform in ['天猫', '京东', '拼多多']:
        p_data = trend_df[trend_df['平台'] == platform]
        p_data = p_data.sort_values('日期')
        ax3.plot(
            p_data['日期'], p_data['销售额'],
            marker='o', linewidth=2.5, markersize=8,
            label=platform,
            color=COLORS.get(platform)
        )
        # 标注数值点
        for _, row in p_data.iterrows():
            ax3.annotate(
                f"{row['销售额']/10000:.1f}万",
                (row['日期'], row['销售额']),
                textcoords="offset points",
                xytext=(0, 12),
                ha='center', fontsize=9
            )
    
    ax3.set_title('近7日销售额趋势', fontsize=18, fontweight='bold', pad=15)
    ax3.set_ylabel('销售额(元)', fontsize=12)
    ax3.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f'{x/10000:.0f}万'))
    ax3.legend(loc='upper left', frameon=True, fontsize=11)
    ax3.tick_params(axis='x', rotation=30)
    ax3.spines['top'].set_visible(False)
    ax3.spines['right'].set_visible(False)
    ax3.grid(True, alpha=0.3)
    
    chart3_path = os.path.join(output_dir, f'chart3_trend_{yesterday}.png')
    fig3.savefig(chart3_path, dpi=150, bbox_inches='tight', facecolor='white')
    plt.close(fig3)
    print(f"[√] 趋势图已保存: {chart3_path}")
    
    # ========== 6. 合成日报长图 ==========
    from PIL import Image
    
    img1 = Image.open(chart1_path)
    img2 = Image.open(chart2_path)
    img3 = Image.open(chart3_path)
    
    # 统一宽度
    target_width = 1200
    img2_resized = img2.resize((target_width, int(img2.height * target_width / img2.width)))
    img1_resized = img1.resize((target_width, int(img1.height * target_width / img1.width)))
    img3_resized = img3.resize((target_width, int(img3.height * target_width / img3.width)))
    
    # 垂直拼接 + 标题区域
    title_height = 80
    total_height = title_height + img1_resized.height + img2_resized.height + img3_resized.height + 60
    report_img = Image.new('RGB', (target_width + 40, total_height), 'white')
    
    # 这里可以用PIL绘制标题文字(略),实际项目中我用了影刀的截图拼接功能
    # 简单拼接方式:
    y_offset = 20
    report_img.paste(img1_resized, (20, y_offset))
    y_offset += img1_resized.height + 10
    report_img.paste(img2_resized, (20, y_offset))
    y_offset += img2_resized.height + 10
    report_img.paste(img3_resized, (20, y_offset))
    
    report_path = os.path.join(output_dir, f'日报_{yesterday}.png')
    report_img.save(report_path, quality=95)
    print(f"[√] 日报长图已合成: {report_path}")
    
    # ========== 7. 返回文件路径给影刀变量 ==========
    SetVariable("report_image_path", report_path)
    SetVariable("chart1_path", chart1_path)
    SetVariable("chart2_path", chart2_path)
    SetVariable("chart3_path", chart3_path)
    
    return report_path


# 执行
report_file = create_daily_report()
print(f"\n日报生成完成!文件路径: {report_file}")
第三步:飞书机器人推送

在影刀中接上一个「HTTP请求」节点,调用飞书机器人Webhook:

在这里插入图片描述

# 飞书消息推送 — Python节点
import requests
import json

report_path = GetVariable("report_image_path")
yesterday = GetVariable("yesterday_date")  # 由前置流程计算好

# 飞书机器人Webhook地址(在飞书群设置→群机器人→添加自定义机器人获取)
WEBHOOK_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxx"

# ========== 方案A:发送图片(需先上传获取image_key)==========
# 第一步:上传图片
upload_url = "https://open.feishu.cn/open-apis/im/v1/images"
# 详细上传逻辑参考飞书文档,这里用方案B更简单

# ========== 方案B:发送卡片消息(推荐)==========
card_msg = {
    "msg_type": "interactive",
    "card": {
        "header": {
            "title": {"tag": "plain_text", "content": f"📊 电商日报 - {yesterday}"},
            "template": "red"
        },
        "elements": [
            {
                "tag": "markdown",
                "content": f"**数据日期**:{yesterday}\n\n"
                           f"**天猫**:销售额8.5万 | 订单1200 | 退货率3.2%\n"
                           f"**京东**:销售额6.2万 | 订单850 | 退货率2.1%\n"
                           f"**拼多多**:销售额4.3万 | 订单950 | 退货率4.5%\n\n"
                           f"详细图表见下方图片 👇"
            },
            {
                "tag": "img",
                "img_key": "img_v2_xxxxxx",  # 需先上传图片获取
                "alt": {"tag": "plain_text", "content": "日报图表"}
            },
            {
                "tag": "action",
                "actions": [{
                    "tag": "button",
                    "text": {"tag": "plain_text", "content": "查看完整报表"},
                    "url": "https://你的BI看板地址",
                    "type": "default"
                }]
            }
        ]
    }
}

resp = requests.post(WEBHOOK_URL, json=card_msg)
print(f"飞书推送结果: {resp.json()}")
第四步:影刀定时任务设置

在影刀应用列表中找到这个流程,点击「定时运行」:

  • 触发方式:每天
  • 执行时间:07:00(在运营上班前一小时跑完)
  • 失败重试:3次,间隔5分钟
  • 通知设置:失败时飞书通知你自己

在这里插入图片描述

注意点

  1. 中文字体问题:这绝对是最大的坑。Windows Server上经常没有中文字体,图表里的中文全部变方框。解决方法:plt.rcParams['font.sans-serif'] 指定多个备选字体;或者在服务器上安装 SimHei.ttf。
  2. matplotlib的Agg后端:影刀的Python节点没有GUI环境,必须设置 matplotlib.use('Agg'),否则报错 TclError: no display name。
  3. 图片尺寸一致性:各平台数据量不同会导致图表大小不一致,合成时用PIL统一resize到固定宽度。
  4. 飞书图片上传:飞书卡片消息里的图片需要先上传获取image_key,不能直接传本地路径。可以先用HTTP请求上传,拿到key后再拼到卡片消息里。

常见问题速查

序号现象原因解决方式
1图表中文显示为方框□□□系统缺少中文字体安装SimHei.ttf到C:\Windows\Fonts;或下载字体文件后用font_manager指定路径
2Python节点报错TclError影刀Python环境无GUI在代码开头设置matplotlib.use('Agg')
3飞书图片推送失败未上传获取image_key先用HTTP请求调用飞书上传图片接口,拿到image_key后再拼消息
4柱状图颜色各不一样未统一配色方案在代码中定义COLORS字典,按平台名称映射固定颜色
在这里插入图片描述

| 5 | 定时任务数据为空 | 采集流程在数据生成前就跑了 | 将定时执行时间设为平台数据更新之后再跑(一般上午8点后) |
| 6 | 图片清晰度不够 | 默认DPI=100太低 | savefig时设置dpi=150或更高 |
| 7 | 饼图标签重叠 | 分类太多或百分比太小 | 小于3%的合并为"其他";或用pctdistance参数调整位置 |
| 8 | excel读取报错No such file | 前置采集流程未完成 | 在Python节点前加一个「判断文件存在」节点,不存在则等待重试 |


推荐资源


内容标签:#RPA #数据可视化 #Python #matplotlib #飞书 #日报自动化 #电商运营

在这里插入图片描述

作者:林焱

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐