Python + Jinja2 + WeasyPrint 实现品牌AI可见度报告自动生成:模板设计、数据聚合与PDF输出
📅 2026/7/22 10:23:13
👁️ 阅读次数
📝 编程学习
问题:品牌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结构如下:
CREATETABLEbrand_mentions(idINTPRIMARYKEYAUTO_INCREMENT,brandVARCHAR(100)NOTNULL,mention_dateDATENOTNULL,mention_countINTDEFAULT0,sentiment_scoreDECIMAL(3,2)COMMENT'情感得分,范围0-1',INDEXidx_brand_date(brand,mention_date));2. 数据聚合
按品牌和时间范围聚合数据,返回列表字典。
defaggregate_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))ifnotrows:return[]return[{'date':row[0],'count':row[1],'sentiment':row[2]}forrowinrows]说明:实际项目中建议使用连接池,并考虑分页或预聚合(如物化视图)应对大数据量。
3. 图表生成
使用Matplotlib生成趋势图,保存为base64嵌入HTML。注意中文字体需指定。
importmatplotlib.pyplotaspltimportbase64fromioimportBytesIOdefgenerate_trend_chart(data):ifnotdata:returnNonedates=[d['date']fordindata]counts=[d['count']fordindata]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()returnimg_base644. 报告模板
使用Jinja2模板,关键区块:封面、摘要卡片、趋势图、数据表。
<!DOCTYPEhtml><html><head><metacharset="utf-8"><style>.card{border:1px solid #ddd;padding:20px;margin:10px;}.trend-chart{width:100%;}</style></head><body><divclass="cover"><h1>{{ brand_name }} AI可见度报告</h1><p>周期:{{ start_date }} 至 {{ end_date }}</p></div><divclass="summary"><divclass="card"><h3>提及率</h3><p>{{ mention_rate }}%</p></div></div><divclass="trend-chart"><imgsrc="data:image/png;base64,{{ trend_chart_base64 }}"/></div></body></html>5. 主流程
defgenerate_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()returnpdf四、验证结果
在开发机(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年的场景,更大规模建议引入消息队列和分布式渲染。
编程学习
技术分享
实战经验