author: 专注Python实战,分享爬虫与数据分析干货
title: Python爬虫实战㉕|数据分析报告自动化,一键生成PDF报告
update: 2026-04-26
tags: Python,报告自动化,PDF,数据分析,matplotlib,报表,jinja2

作者:专注Python实战,分享爬虫与数据分析干货
更新时间:2026年4月
适合人群:已掌握爬虫+Pandas+可视化,想做报告自动化的开发者


前言:每周做报表?让Python替你干!

老板每周要看数据报告:销量趋势、品类占比、异常数据。手动做PPT/Excel太累。

Python自动分析 + 自动画图 + 自动生成报告 = 解放双手。


一、报告自动化流程

原始数据(CSV/DB)
    ↓
Pandas数据清洗
    ↓
Pandas统计分析
    ↓
Matplotlib/Seaborn画图
    ↓
生成HTML/PDF报告

二、数据准备

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta

# 配置
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
sns.set_theme(style="whitegrid", font="SimHei")

# 模拟数据
np.random.seed(42)
dates = pd.date_range("2026-01-01", "2026-03-31", freq="D")
df = pd.DataFrame({
    "日期": dates,
    "品类": np.random.choice(["手机", "电脑", "耳机", "键盘", "显示器"], len(dates)),
    "城市": np.random.choice(["北京", "上海", "广州", "深圳"], len(dates)),
    "销量": np.random.randint(10, 200, len(dates)),
    "金额": np.random.uniform(100, 8000, len(dates)).round(2),
})

df["月份"] = df["日期"].dt.to_period("M")
df.to_csv("report_data.csv", index=False, encoding="utf-8-sig")

三、自动分析

class DataAnalyzer:
    """数据分析器"""

    def __init__(self, df):
        self.df = df
        self.stats = {}

    def run_all(self):
        """运行所有分析"""
        self.basic_stats()
        self.trend_analysis()
        self.category_analysis()
        self.city_analysis()
        self.anomaly_detection()
        return self.stats

    def basic_stats(self):
        """基础统计"""
        self.stats["总销量"] = self.df["销量"].sum()
        self.stats["总金额"] = self.df["金额"].sum()
        self.stats["平均客单价"] = self.df["金额"].mean()
        self.stats["数据天数"] = self.df["日期"].nunique()
        self.stats["品类数"] = self.df["品类"].nunique()

    def trend_analysis(self):
        """趋势分析"""
        monthly = self.df.groupby("月份").agg(
            总销量=("销量", "sum"),
            总金额=("金额", "sum"),
        )
        self.stats["月度趋势"] = monthly

    def category_analysis(self):
        """品类分析"""
        cat = self.df.groupby("品类").agg(
            总销量=("销量", "sum"),
            总金额=("金额", "sum"),
            平均单价=("金额", "mean"),
        ).sort_values("总金额", ascending=False)
        self.stats["品类统计"] = cat

    def city_analysis(self):
        """城市分析"""
        city = self.df.groupby("城市").agg(
            总销量=("销量", "sum"),
            总金额=("金额", "sum"),
        ).sort_values("总金额", ascending=False)
        self.stats["城市统计"] = city

    def anomaly_detection(self):
        """异常检测"""
        Q1 = self.df["金额"].quantile(0.25)
        Q3 = self.df["金额"].quantile(0.75)
        IQR = Q3 - Q1
        lower = Q1 - 1.5 * IQR
        upper = Q3 + 1.5 * IQR
        anomalies = self.df[(self.df["金额"] < lower) | (self.df["金额"] > upper)]
        self.stats["异常数据"] = anomalies
        self.stats["异常数量"] = len(anomalies)

四、自动画图

class ChartGenerator:
    """图表生成器"""

    def __init__(self, df, output_dir="charts"):
        self.df = df
        self.output_dir = output_dir
        import os
        os.makedirs(output_dir, exist_ok=True)

    def generate_all(self):
        """生成所有图表"""
        self.trend_chart()
        self.category_chart()
        self.city_chart()
        self.distribution_chart()

    def trend_chart(self):
        monthly = self.df.groupby(self.df["日期"].dt.to_period("M")).agg(
            总销量=("销量", "sum"), 总金额=("金额", "sum")
        )
        monthly.index = monthly.index.astype(str)

        fig, ax1 = plt.subplots(figsize=(10, 5))
        ax1.bar(monthly.index, monthly["总销量"], color="#4472C4", alpha=0.7, label="销量")
        ax1.set_ylabel("销量", color="#4472C4")
        ax2 = ax1.twinx()
        ax2.plot(monthly.index, monthly["总金额"], "ro-", label="金额")
        ax2.set_ylabel("金额", color="red")
        plt.title("月度销售趋势")
        plt.tight_layout()
        plt.savefig(f"{self.output_dir}/trend.png", dpi=150, bbox_inches="tight")
        plt.close()

    def category_chart(self):
        cat = self.df.groupby("品类")["金额"].sum().sort_values(ascending=False)
        plt.figure(figsize=(8, 5))
        plt.pie(cat.values, labels=cat.index, autopct="%1.1f%%",
                colors=sns.color_palette("Set2"))
        plt.title("品类销售占比")
        plt.tight_layout()
        plt.savefig(f"{self.output_dir}/category.png", dpi=150, bbox_inches="tight")
        plt.close()

    def city_chart(self):
        city = self.df.groupby("城市")["销量"].sum().sort_values()
        plt.figure(figsize=(8, 5))
        city.plot(kind="barh", color="#4472C4")
        plt.title("各城市销量")
        plt.tight_layout()
        plt.savefig(f"{self.output_dir}/city.png", dpi=150, bbox_inches="tight")
        plt.close()

    def distribution_chart(self):
        plt.figure(figsize=(8, 5))
        sns.histplot(self.df["金额"], kde=True, color="#4472C4")
        plt.title("金额分布")
        plt.tight_layout()
        plt.savefig(f"{self.output_dir}/distribution.png", dpi=150, bbox_inches="tight")
        plt.close()

五、生成HTML报告

def generate_html_report(stats, charts_dir="charts", output="report.html"):
    """生成HTML报告"""

    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>数据分析报告</title>
        <style>
            body {{ font-family: "Microsoft YaHei", sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; }}
            h1 {{ color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; }}
            h2 {{ color: #34495e; margin-top: 30px; }}
            .stat-grid {{ display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }}
            .stat-card {{ background: #f8f9fa; border-radius: 8px; padding: 15px; text-align: center; }}
            .stat-value {{ font-size: 24px; font-weight: bold; color: #2c3e50; }}
            .stat-label {{ font-size: 14px; color: #7f8c8d; }}
            img {{ max-width: 100%; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin: 10px 0; }}
            table {{ border-collapse: collapse; width: 100%; margin: 10px 0; }}
            th, td {{ border: 1px solid #ddd; padding: 8px 12px; text-align: left; }}
            th {{ background: #3498db; color: white; }}
            tr:nth-child(even) {{ background: #f2f2f2; }}
        </style>
    </head>
    <body>
        <h1>📊 数据分析报告</h1>
        <p>生成时间:{datetime.now().strftime("%Y-%m-%d %H:%M")}</p>

        <h2>📌 核心指标</h2>
        <div class="stat-grid">
            <div class="stat-card">
                <div class="stat-value">{stats["总销量"]:,}</div>
                <div class="stat-label">总销量</div>
            </div>
            <div class="stat-card">
                <div class="stat-value">¥{stats["总金额"]:,.0f}</div>
                <div class="stat-label">总金额</div>
            </div>
            <div class="stat-card">
                <div class="stat-value">¥{stats["平均客单价"]:,.0f}</div>
                <div class="stat-label">平均客单价</div>
            </div>
        </div>

        <h2>📈 趋势分析</h2>
        <img src="{charts_dir}/trend.png" alt="趋势图">

        <h2>🏷️ 品类分析</h2>
        <img src="{charts_dir}/category.png" alt="品类图">

        <h2>🏙️ 城市分析</h2>
        <img src="{charts_dir}/city.png" alt="城市图">

        <h2>📊 金额分布</h2>
        <img src="{charts_dir}/distribution.png" alt="分布图">

        <h2>⚠️ 异常数据</h2>
        <p>检测到 <strong>{stats["异常数量"]}</strong> 条异常数据</p>
    </body>
    </html>
    """

    with open(output, "w", encoding="utf-8") as f:
        f.write(html)
    print(f"报告已生成: {output}")

六、一键执行

# 完整流程
df = pd.read_csv("report_data.csv", encoding="utf-8-sig")

analyzer = DataAnalyzer(df)
stats = analyzer.run_all()

charts = ChartGenerator(df)
charts.generate_all()

generate_html_report(stats)
print("报告生成完成!")

七、知识卡

概念 说明
DataAnalyzer 数据分析器类
ChartGenerator 图表生成器类
generate_html_report() HTML报告生成
plt.savefig() 保存图表
bbox_inches=“tight” 去除空白边距
HTML模板 jinja2或f-string

八、课后作业

必做题:

  1. 编写数据分析类,输出统计结果
  2. 自动生成4种图表
  3. 生成HTML格式报告

选做题:

  1. 将HTML报告转PDF
  2. 定时自动执行报告生成

有问题欢迎评论区留言,大家一起讨论!


标签:Python | 报告自动化 | PDF | 数据分析 | 报表 | HTML报告

更多推荐