Python + Jinja2 + WeasyPrint 实现品牌AI可见度报告自动生成:模板设计、数据聚合与PDF输出
·
问题:品牌AI可见度监测需要定期生成报告,手动从数据库导出数据、用Excel处理、再用PPT制作,每次耗时2-3天,且格式不统一。本文介绍使用 Python + Jinja2 + WeasyPrint 实现自动化报告生成,涵盖模板设计、数据聚合、图表生成和PDF输出,读者可据此搭建基础框架。
一、问题与原因
初始方案是手动流程:从数据库导出品牌提及数据,在Excel中计算指标,再复制到PPT中绘制图表。主要痛点:
- 数据源分散:品牌提及次数、情感分析得分、趋势数据存储在不同表中
- 报告模板不固定:不同客户或不同周期的报告结构有差异
- 图表生成繁琐:每次需手动绘制趋势图、对比图
- 重复劳动:每次报告需2-3天,且容易出错
二、技术选型
对比了四种方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| Python + ReportLab | 灵活,完全控制PDF布局 | 开发量大,图表需自行绘制 |
| Python + Jinja2 + WeasyPrint | 模板化,支持HTML转PDF | 复杂布局需CSS调试 |
| Node.js + Puppeteer | 可直接渲染前端图表 | 依赖浏览器环境,资源占用高 |
| 商业报表工具(如FineReport) | 开箱即用 | 成本高,定制受限 |
最终选择 Python + Jinja2 + WeasyPrint,原因:团队Python技术栈,模板化易于维护,图表使用Matplotlib生成图片嵌入。
三、核心实现
1. 数据表结构
假设品牌提及数据表 brand_mentions 结构如下:
CREATE TABLE brand_mentions (
id INT PRIMARY KEY AUTO_INCREMENT,
brand VARCHAR(100) NOT NULL,
mention_date DATE NOT NULL,
mention_count INT DEFAULT 0,
sentiment_score DECIMAL(3,2) COMMENT '情感得分,范围0-1',
INDEX idx_brand_date (brand, mention_date)
);
2. 数据聚合
按品牌和时间范围聚合数据,返回列表字典。
def aggregate_data(brand, start_date, end_date):
query = """
SELECT mention_date, mention_count, sentiment_score
FROM brand_mentions
WHERE brand = %s AND mention_date BETWEEN %s AND %s
ORDER BY mention_date
"""
rows = db.execute(query, (brand, start_date, end_date))
if not rows:
return []
return [{'date': row[0], 'count': row[1], 'sentiment': row[2]} for row in rows]
说明:实际项目中建议使用连接池,并考虑分页或预聚合(如物化视图)应对大数据量。
3. 图表生成
使用Matplotlib生成趋势图,保存为base64嵌入HTML。注意中文字体需指定。
import matplotlib.pyplot as plt
import base64
from io import BytesIO
def generate_trend_chart(data):
if not data:
return None
dates = [d['date'] for d in data]
counts = [d['count'] for d in data]
plt.figure(figsize=(10, 4))
plt.plot(dates, counts, marker='o')
plt.title('品牌提及趋势')
plt.xlabel('日期')
plt.ylabel('提及次数')
plt.grid(True)
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
buf = BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
img_base64 = base64.b64encode(buf.read()).decode('utf-8')
plt.close()
return img_base64
4. 报告模板
使用Jinja2模板,关键区块:封面、摘要卡片、趋势图、数据表。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
.card { border: 1px solid #ddd; padding: 20px; margin: 10px; }
.trend-chart { width: 100%; }
</style>
</head>
<body>
<div class="cover">
<h1>{{ brand_name }} AI可见度报告</h1>
<p>周期:{{ start_date }} 至 {{ end_date }}</p>
</div>
<div class="summary">
<div class="card">
<h3>提及率</h3>
<p>{{ mention_rate }}%</p>
</div>
</div>
<div class="trend-chart">
<img src="data:image/png;base64,{{ trend_chart_base64 }}" />
</div>
</body>
</html>
5. 主流程
def generate_report(brand, start_date, end_date):
data = aggregate_data(brand, start_date, end_date)
trend_chart = generate_trend_chart(data)
context = {
'brand_name': brand,
'start_date': start_date,
'end_date': end_date,
'mention_rate': round(calculate_mention_rate(data), 2),
'trend_chart_base64': trend_chart,
}
html = render_template('report_template.html', **context)
pdf = weasyprint.HTML(string=html).write_pdf()
return pdf
四、验证结果
在开发机(CPU 4核,内存16GB,MySQL 8.0)上测试,生成一份包含10个品牌、30天数据的报告:
- 数据聚合:约2秒(索引优化后)
- 图表生成:约3秒(10张图)
- PDF渲染:约5秒
- 总耗时:约10秒
注意:具体性能取决于数据量和硬件配置,上述数据仅为单次测试参考。
验证方法:检查PDF是否包含所有品牌章节、图表是否正常显示、数据是否与数据库一致。
五、踩坑与边界
- 中文字体:Matplotlib默认不支持中文,需指定中文字体(如SimHei),否则图表中文显示为方框。
- 图表尺寸:PDF页面宽度固定,图表尺寸需提前计算,避免溢出。
- 大数据量:当数据量超过10万行时,聚合查询可能变慢,建议使用预聚合表或分页查询。
- 模板版本:模板变更后需重新生成所有报告,建议对模板进行版本管理。
六、总结
本文解决了品牌AI可见度报告手动生成效率低的问题,采用 Python + Jinja2 + WeasyPrint 方案实现自动化。根因在于数据分散和重复劳动,最终通过模板化、数据聚合和图表自动生成解决。该方案适用于品牌数少于50、周期小于1年的场景,更大规模建议引入消息队列和分布式渲染。
更多推荐

所有评论(0)