Python自動(dòng)化辦公-玩轉(zhuǎn)報(bào)表
你很可能也看到過(guò)公眾號(hào)文章下方的廣告,是關(guān)于 Python 自動(dòng)化生成報(bào)表的,我自己就經(jīng)常看到,說(shuō)的是一個(gè)人因?yàn)閳?bào)表做不出來(lái),愁眉苦臉,做不出來(lái)就要被辭職了,這時(shí)一個(gè)高手拿過(guò)電腦,一頓操作猛如虎,一份精美的報(bào)表很快就生成了,被幫助的人寫(xiě)滿(mǎn)一臉的崇拜...
其實(shí)不用崇拜,很簡(jiǎn)單,今天,我來(lái)告訴你方法。
這里說(shuō)做報(bào)表,不用 excel,不用專(zhuān)業(yè)的報(bào)表平臺(tái),就是純 html 生成一些漂亮的可視化報(bào)表,甚至可以交互,這樣的報(bào)表你可以發(fā) html 郵件給老板,老板不需要下載,不需要登陸專(zhuān)業(yè)的報(bào)表平臺(tái),打開(kāi)郵件就可以直接看到,還可以點(diǎn)擊來(lái)交互,非常方便,如果你這樣做,那離升職加薪不會(huì)太遠(yuǎn)??。
廢話(huà)不多說(shuō),現(xiàn)在就告訴你方法。咱們以目標(biāo)為導(dǎo)向,技術(shù)不技術(shù)的不重要,實(shí)現(xiàn)了就好。
1、選擇一個(gè)報(bào)表模版
首先你要生成什么樣的報(bào)表,這里有個(gè)網(wǎng)站可以供你選擇,就是大名鼎鼎的 ECharts 庫(kù),https://echarts.apache.org/zh/index.html

點(diǎn)擊所有示例,可以看到很多樣例圖片:

選擇一個(gè)你想用的,比如,我們選擇「柱狀圖」->「基礎(chǔ)柱狀圖」

點(diǎn)擊「下載示例」,就可以下載一個(gè) html 文件,用瀏覽器打開(kāi)就是這個(gè)柱狀圖。

現(xiàn)在用個(gè)編輯器打開(kāi)這個(gè) html 文件,修改其中的數(shù)據(jù),我們就可以生成一個(gè)屬于自己的報(bào)表

然后保存,這樣一個(gè)報(bào)表就生成了,如果簡(jiǎn)陋一點(diǎn)的話(huà),你直接把這個(gè) html 作為郵件的附件發(fā)給老板,老板雙擊這個(gè) html 文件就可以在瀏覽器上看到,不過(guò)這并不是完美的,萬(wàn)一老板的電腦沒(méi)有瀏覽器呢。
完美的解決方案是將這些報(bào)表顯示在郵件的正文。
2、用 Python 發(fā)送 html 郵件
這個(gè) 前文最簡(jiǎn)單的方式發(fā)送郵件 小節(jié) "發(fā)送多彩的 html 郵件" 有有具體的方法和代碼,這里就不重復(fù)說(shuō)了。
3、使用 Jinja2 來(lái)渲染 html
第 1 步中的替換是手工操作的,假如數(shù)據(jù)量比較大,或者要批量生成報(bào)表,可能就沒(méi)那么方便,這不,我們有萬(wàn)能的 Python 嘛。
如果用過(guò) Django,你就知道 Jinja2 的模版大法,簡(jiǎn)單來(lái)講,Jinja2 將一個(gè)文件中的標(biāo)識(shí)替換成你需要的內(nèi)容。這里我們用的正是這一點(diǎn)。
比如 html 文件中的有這么一段:
option = {
xAxis: {
type: 'category',
data: ['張三', '李四', '王五', '趙六']
},
yAxis: {
type: 'value'
},
series: [{
data: [120, 200, 150, 80],
type: 'bar'
}]
};
我們希望替換其中兩處的 data,就可以先這樣寫(xiě)
option = {
xAxis: {
type: 'category',
data: {{ data1 }}
},
yAxis: {
type: 'value'
},
series: [{
data: {{ data2 }},
type: 'bar'
}]
};
然后借助 Jinja2 可以很方便的替換:
from templater import DefaultTemplater
if __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)
打開(kāi) bar-simple-templeted.html 發(fā)現(xiàn)已經(jīng)被替換掉了,在批量制作報(bào)表時(shí),是不是很方便?

這里用到了 DefaultTemplater,其實(shí)現(xiàn)代碼如下 (templater.py):
# templater.py
from dataclasses import dataclass
from typing import Dict
from jinja2 import Template
@dataclass
class DefaultTemplater(object):
""" Allow to inject data in a jinja2 templated file and write the result to specified destination """
source: str
destination: str
def render(self, data: Dict) -> None:
""" Write template from source filled with data to destination
Args:
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 values
Args:
values: dict with key: tag to search in template, value: value to replace the tag
"""
template = Template(self.template)
templated = template.render(**values)
return templated
def write_filled_template(self, content: str):
"""Write the result of the template and injected value to destination
Args:
content: what to write
"""
with open(self.destination, "w") as f:
f.write(content)
比如說(shuō)一些更酷炫的:
其 html 代碼如下(與 echarts 上下載的略有調(diào)整):
<!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 的分類(lèi)就可以實(shí)現(xiàn)自己想要的動(dòng)態(tài)報(bào)表。
最好懂一點(diǎn) javascript 的語(yǔ)法,這樣可以改一改數(shù)據(jù)的結(jié)構(gòu),更方便的生成自己想要的報(bào)表。
最后的話(huà)
要生成報(bào)表,其實(shí)并不需要太精通技術(shù),從網(wǎng)上下載個(gè)模版,自己改下數(shù)據(jù)和分類(lèi),就可以制作一個(gè) html 報(bào)表,然后將 html 作為郵件正文發(fā)送出去,也可以轉(zhuǎn)成圖片、pdf 發(fā)送,具體場(chǎng)景就看自己需求了。如果要批量制作很多同類(lèi)報(bào)表,可以借助 Jinja2 的模版大法,批量替換報(bào)表中的數(shù)據(jù),更高效的完成。
如果你也在學(xué)習(xí) Python,不妨關(guān)注一下我,每天學(xué)習(xí)一個(gè) Python 技巧。
推薦閱讀:
有問(wèn)題?留言討論
