你很可能也看到过公众号文章下方的关于 Python 自动化生成报表的广告,因为我自己就经常看到,说的是一个人因为报表做不出来,一筹莫展,马上要被辞职了,这时一个高手拿过电脑,一顿操作猛如虎,然后一份精美的报表就生成了,被帮助的人写满一脸的崇拜。
其实不用崇拜,很简单,今天,我来告诉你方法。
这里说做报表,不用 excel,不用专业的报表平台,就是纯 html 生成一些漂亮的可视化报表,甚至可以交互,这样的报表你可以发 html 邮件给老板,老板不需要下载,不需要登陆专业的报表平台,打开邮件就可以直接看到,还可以点击来交互,非常方便,如果你这样做,那离升职加薪不会太远👍。
废话不多说,现在就告诉你方法。咱们以目标为导向,技术不技术的不重要,实现了就好。
选择一个报表模版
首先你要生成什么样的报表,这里有个网站可以供你选择,就是大名鼎鼎的 ECharts 库,https://echarts.apache.org/zh/index.html

点击所有示例,可以看到很多样例图片:

选择一个你想用的,比如,我们选择「柱状图」->「基础柱状图」

点击「下载示例」,就可以下载一个 html 文件,用浏览器打开就是这个柱状图。

现在用个编辑器打开这个 html 文件,修改其中的数据,我们就可以生成一个属于自己的报表

然后保存,这样一个报表就生成了,如果简陋一点的话,你直接把这个 html 作为邮件的附件发给老板,老板双击这个 html 文件就可以在浏览器上看到,不过这并不是完美的,万一老板的电脑没有浏览器呢。
完美的解决方案是将这些报表显示在邮件的正文。
用 Python 发送 html 邮件
这个前文最简单的方式发送邮件,让程序出错自动发邮件 小节 “发送多彩的 html 邮件” 有说道,这里就不多说了。
使用 Jinja2 来渲染 html
第一步中的替换是手工操作的,假如数据量比较大,可能就没那么方便,这不,我们有万能的 Python 嘛。
如果用过 Django,你就知道 Jinja2 的模版大法,简单来讲,Jinja2 讲一个文件中的标识替换成你需要的内容。这里我们用的正是这一点。
比如 html 文件中的有这么一段:
option = {xAxis: {type: 'category',data: ['张三', '李四', '王五', '赵六']},yAxis: {type: 'value'},series: [{data: [120, 200, 150, 80],type: 'bar'}]};
我们希望替换其中两处的 data,就可以先这样写
option = {xAxis: {type: 'category',data: {{ data1 }}},yAxis: {type: 'value'},series: [{data: {{ data2 }},type: 'bar'}]};
然后借助 Jinja2 可以很方便的替换:
from templater import DefaultTemplaterif __name__ == "__main__":templater = DefaultTemplater("bar-simple.html", "bar-simple-templeted.html")data1 = ['张三1', '李四2', '王五3', '赵六4']data2 = [3120, 3200, 3150, 980]tags = {"data1": data1,"data2": data2,}templater.render(tags)
打开 bar-simple-templeted.html 发现已经被替换掉了,在批量制作报表时,是不是很方便?

这里用到了 DefaultTemplater,其实现代码如下 (templater.py):
# templater.pyfrom dataclasses import dataclassfrom typing import Dictfrom jinja2 import Template@dataclassclass DefaultTemplater(object):""" Allow to inject data in a jinja2 templated file and write the result to specified destination """source: strdestination: strdef render(self, data: Dict) -> None:""" Write template from source filled with data to destinationArgs:data: the data to inject in the template"""self.load_template()filled_template = self.replace(data)self.write_filled_template(filled_template)def load_template(self) -> None:""" Load template from source"""with open(self.source, "r") as f:self.template = f.read()def replace(self, values: Dict) -> str:""" Replace tag in template with valuesArgs:values: dict with key: tag to search in template, value: value to replace the tag"""template = Template(self.template)templated = template.render(**values)return templateddef write_filled_template(self, content: str):"""Write the result of the template and injected value to destinationArgs:content: what to write"""with open(self.destination, "w") as f:f.write(content)
比如说一些更复杂的
其 html 代码如下(与 echarts 上下载的略有调整):
<!DOCTYPE html><html style="height: 100%"><head><meta charset="utf-8"></head><body style="height: 100%; margin: 0"><div id="container" style="height: 100%"></div><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script><script type="text/javascript">var dom = document.getElementById("container");var myChart = echarts.init(dom);var app = {};var option;var data_list = [[1, 2, 3, 4, 5],[8, 4, 3, 2, 3],[5, 9, 6, 10, 3],[9, 7, 4, 7, 6],[5, 4, 3, 2, 9]]option = {xAxis: {max: 'dataMax',},yAxis: {type: 'category',data: ['A', 'B', 'C', 'D', 'E'],inverse: true,animationDuration: 300,animationDurationUpdate: 300,max: 4 // only the largest 3 bars will be displayed},series: [{realtimeSort: true,name: 'X',type: 'bar',data: data_list[0],label: {show: true,position: 'right',valueAnimation: true}}],legend: {show: true},animationDuration: 0,animationDurationUpdate: 3000,animationEasing: 'linear',animationEasingUpdate: 'linear'};var index = 1;function run () {var data = option.series[0].data;if(index >= data_list.length){return;}for (var i = 0; i < data.length; ++i) {data[i] = data_list[index][i];}index++;myChart.setOption(option);}setInterval(function () {run();}, 3000);if (option && typeof option === 'object') {myChart.setOption(option);}</script></body></html>
你只需要修改 data_list 和 yAxis 的分类就可以实现自己想要的动态报表。最好懂一点 javascript 的语法,这样可以改一改数据的结构,更方便的生成自己想要的报表。
最后的话
要生成报表,其实并不需要太精通技术,从网上下载个模版,自己改改数据,改改分类,就可以制作一个 html 报表,然后可以将 html 作为邮件正文发送出去,也可以生成图片,pdf 发送,具体场景就看自己需求了。如果要批量制作很多同类报表,可以借助 Jinja2 的模版大法,批量替换报表中的数据,更高效的完成。
