1.1. 前言
眾所周知,安服工程師又叫做Word工程師停做,在打工或者批量SRC的時候,如果產(chǎn)出很多大莫,又需要一個一個的寫報告的情況下會非常的折磨人蛉腌,因此查了一些相關(guān)的資料,發(fā)現(xiàn)使用python的docxtpl
庫批量寫報告效果很不錯只厘,記錄一下烙丛。
1.2. 介紹
docxtpl
是一個用于生成 Microsoft Word 文檔的模板引擎庫,它結(jié)合了 docx 模塊和 Jinja2 模板引擎羔味,使用戶能夠使用 Microsoft Word 模板文件并在其中填充動態(tài)數(shù)據(jù)河咽。它提供了一種方便的方式來生成個性化的 Word 文檔,并支持條件語句介评、循環(huán)語句和變量等控制結(jié)構(gòu)库北,以滿足不同的文檔生成需求爬舰。
官方GitHub地址:https://github.com/elapouya/python-docx-template
官方文檔地址:https://docxtpl.readthedocs.io/en/latest/
簡單來說:就是創(chuàng)建一個類似Jinja2語法的模板文檔,然后往里面動態(tài)填充內(nèi)容就可以了
安裝:
pip3 install docxtpl
1.3. 基礎(chǔ)使用
from docxtpl import DocxTemplate
doc = DocxTemplate("test.docx")
context = {'whoami': "d4m1ts"}
doc.render(context)
doc.save("generated_doc.docx")
其中寒瓦,test.docx內(nèi)容如下:
生成后的結(jié)果如下:
1.4. 案例介紹
1.4.1. 需求假設(shè)
寫一份不考慮美觀的漏掃報告情屹,需要有統(tǒng)計結(jié)果圖和漏洞詳情,每個漏洞包括漏洞名杂腰、漏洞地址垃你、漏洞等級、漏洞危害喂很、復(fù)現(xiàn)過程惜颇、修復(fù)建議六個部分。
1.4.2. 模板文檔準(zhǔn)備
編寫的模板文檔如下少辣,使用到了常見的if
凌摄、for
、賦值
等漓帅,保存為template.docx
锨亏,后續(xù)只需要向里面填充數(shù)據(jù)即可。
1.4.3. 數(shù)據(jù)結(jié)構(gòu)分析
傳入數(shù)據(jù)需要一串json字符串忙干,因此我們根據(jù)模板文檔梳理好json結(jié)構(gòu)器予,然后傳入即可。
梳理好的數(shù)據(jù)結(jié)構(gòu)如下:
{
"餅圖": "111",
"柱狀圖": "222",
"漏洞簡報": [
{
"漏洞名": "測試漏洞名1",
"漏洞等級": "高危"
}
],
"漏洞詳情": [
{
"漏洞名": "測試漏洞名1",
"漏洞地址": "http://blog.gm7.org/",
"漏洞等級": "高危",
"漏洞危害": "危害XXX",
"復(fù)現(xiàn)過程": "先xxx捐迫,再xxx乾翔,最后xxx",
"修復(fù)建議": "更新到最新版本即可"
}
]
}
編寫代碼測試一下可行性:
from docxtpl import DocxTemplate
doc = DocxTemplate("template.docx")
context = {
"餅圖": "111",
"柱狀圖": "222",
"漏洞簡報": [
{
"漏洞名": "測試漏洞名1",
"漏洞等級": "高危"
},
{
"漏洞名": "測試漏洞名2",
"漏洞等級": "嚴(yán)重"
},
{
"漏洞名": "測試漏洞名2",
"漏洞等級": "中危"
}
],
"漏洞詳情": [
{
"漏洞名": "測試漏洞名1",
"漏洞地址": "http://blog.gm7.org/",
"漏洞等級": "高危",
"漏洞危害": "危害XXX",
"復(fù)現(xiàn)過程": "先xxx,再xxx施戴,最后xxx",
"修復(fù)建議": "更新到最新版本即可"
},
{
"漏洞名": "測試漏洞名2",
"漏洞地址": "http://bblog.gm7.org/",
"漏洞等級": "嚴(yán)重",
"漏洞危害": "危害XXX",
"復(fù)現(xiàn)過程": "先xxx反浓,再xxx,最后xxx",
"修復(fù)建議": "更新到最新版本即可"
},
{
"漏洞名": "測試漏洞名3",
"漏洞地址": "http://cblog.gm7.org/",
"漏洞等級": "中危",
"漏洞危害": "危害XXX",
"復(fù)現(xiàn)過程": "先xxx暇韧,再xxx勾习,最后xxx",
"修復(fù)建議": "更新到最新版本即可"
}
]
}
doc.render(context)
doc.save("generated_doc.docx")
很好浓瞪,達到了預(yù)期的效果懈玻。
1.4.4. 加入圖表
在上面的過程中,內(nèi)容幾乎是沒問題了乾颁,但是圖表還是沒有展示出來涂乌。生成圖表我們使用plotly
這個庫,并將生成內(nèi)容寫入ByteIO
英岭。
相關(guān)代碼如下:
import plotly.graph_objects as go
from io import BytesIO
def generatePieChart(title: str, labels: list, values: list, colors: list):
"""
生成餅圖
https://juejin.cn/post/6911701157647745031#heading-3
https://juejin.cn/post/6950460207860449317#heading-5
:param title: 餅圖標(biāo)題
:param labels: 餅圖標(biāo)簽
:param values: 餅圖數(shù)據(jù)
:param colors: 餅圖每塊的顏色
:return:
"""
# 基礎(chǔ)餅圖
fig = go.Figure(data=[go.Pie(
labels=labels,
values=values,
hole=.4, # 中心環(huán)大小
insidetextorientation="horizontal"
)])
# 更新顏色
fig.update_traces(
textposition='inside', # 文本顯示位置
hoverinfo='label+percent', # 懸停信息
textinfo='label+percent', # 餅圖中顯示的信息
textfont_size=15,
marker=dict(colors=colors)
)
# 更新標(biāo)題
fig.update_layout(
title={ # 設(shè)置整個標(biāo)題的名稱和位置
"text": title,
"y": 0.96, # y軸數(shù)值
"x": 0.5, # x軸數(shù)值
"xanchor": "center", # x湾盒、y軸相對位置
"yanchor": "top"
}
)
image_io = BytesIO()
fig.write_image(image_io, format="png")
return image_io
def generateBarChart(title: str, x: list, y: list):
"""
生成柱狀圖
https://cloud.tencent.com/developer/article/1817208
https://blog.csdn.net/qq_25443541/article/details/115999537
https://blog.csdn.net/weixin_45826022/article/details/122912484
:param title: 標(biāo)題
:param x: 柱狀圖標(biāo)簽
:param y: 柱狀圖數(shù)據(jù)
:return:
"""
# x軸長度最為18
b = x
x = []
for i in b:
if len(i) >= 18:
x.append(f"{i[:15]}...")
else:
x.append(i)
# 基礎(chǔ)柱狀圖
fig = go.Figure(data=[go.Bar(
x=x,
y=y,
text=y,
textposition="outside",
marker=dict(color=["#3498DB"] * len(y)),
width=0.3
)])
# 更新標(biāo)題
fig.update_layout(
title={ # 設(shè)置整個標(biāo)題的名稱和位置
"text": title,
"y": 0.96, # y軸數(shù)值
"x": 0.5, # x軸數(shù)值
"xanchor": "center", # x、y軸相對位置
"yanchor": "top"
},
xaxis_tickangle=-45, # 傾斜45度
plot_bgcolor='rgba(0,0,0,0)' # 背景透明
)
fig.update_xaxes(
showgrid=False
)
fig.update_yaxes(
zeroline=True,
zerolinecolor="#17202A",
zerolinewidth=1,
showgrid=True,
gridcolor="#17202A",
showline=True
)
image_io = BytesIO()
fig.write_image(image_io, format="png")
return image_io
1.4.5. 最終結(jié)果
要插入圖片內(nèi)容诅妹,代碼語法如下:
myimage = InlineImage(tpl, image_descriptor='test_files/python_logo.png', width=Mm(20), height=Mm(10))
完整代碼如下:
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
import plotly.graph_objects as go
from io import BytesIO
def generatePieChart(title: str, labels: list, values: list, colors: list):
"""
生成餅圖
https://juejin.cn/post/6911701157647745031#heading-3
https://juejin.cn/post/6950460207860449317#heading-5
:param title: 餅圖標(biāo)題
:param labels: 餅圖標(biāo)簽
:param values: 餅圖數(shù)據(jù)
:param colors: 餅圖每塊的顏色
:return:
"""
# 基礎(chǔ)餅圖
fig = go.Figure(data=[go.Pie(
labels=labels,
values=values,
hole=.4, # 中心環(huán)大小
insidetextorientation="horizontal"
)])
# 更新顏色
fig.update_traces(
textposition='inside', # 文本顯示位置
hoverinfo='label+percent', # 懸停信息
textinfo='label+percent', # 餅圖中顯示的信息
textfont_size=15,
marker=dict(colors=colors)
)
# 更新標(biāo)題
fig.update_layout(
title={ # 設(shè)置整個標(biāo)題的名稱和位置
"text": title,
"y": 0.96, # y軸數(shù)值
"x": 0.5, # x軸數(shù)值
"xanchor": "center", # x罚勾、y軸相對位置
"yanchor": "top"
}
)
image_io = BytesIO()
fig.write_image(image_io, format="png")
return image_io
def generateBarChart(title: str, x: list, y: list):
"""
生成柱狀圖
https://cloud.tencent.com/developer/article/1817208
https://blog.csdn.net/qq_25443541/article/details/115999537
https://blog.csdn.net/weixin_45826022/article/details/122912484
:param title: 標(biāo)題
:param x: 柱狀圖標(biāo)簽
:param y: 柱狀圖數(shù)據(jù)
:return:
"""
# x軸長度最為18
b = x
x = []
for i in b:
if len(i) >= 18:
x.append(f"{i[:15]}...")
else:
x.append(i)
# 基礎(chǔ)柱狀圖
fig = go.Figure(data=[go.Bar(
x=x,
y=y,
text=y,
textposition="outside",
marker=dict(color=["#3498DB"] * len(y)),
width=0.3
)])
# 更新標(biāo)題
fig.update_layout(
title={ # 設(shè)置整個標(biāo)題的名稱和位置
"text": title,
"y": 0.96, # y軸數(shù)值
"x": 0.5, # x軸數(shù)值
"xanchor": "center", # x毅人、y軸相對位置
"yanchor": "top"
},
xaxis_tickangle=-45, # 傾斜45度
plot_bgcolor='rgba(0,0,0,0)' # 背景透明
)
fig.update_xaxes(
showgrid=False
)
fig.update_yaxes(
zeroline=True,
zerolinecolor="#17202A",
zerolinewidth=1,
showgrid=True,
gridcolor="#17202A",
showline=True
)
image_io = BytesIO()
fig.write_image(image_io, format="png")
return image_io
doc = DocxTemplate("template.docx")
context = {
"餅圖": InlineImage(doc, image_descriptor=generatePieChart(
title="漏洞數(shù)量",
labels=["嚴(yán)重", "高危", "中危", "低危"],
values=[1, 1, 1, 0],
colors=["#8B0000", "red", "orange", "aqua"]
), width=Mm(130)),
"柱狀圖": InlineImage(doc, image_descriptor=generateBarChart(
title="漏洞類型",
x=["測試漏洞名1", "測試漏洞名2", "測試漏洞名3"],
y=[1, 1, 1]
), width=Mm(130)),
"漏洞簡報": [
{
"漏洞名": "測試漏洞名1",
"漏洞等級": "高危"
},
{
"漏洞名": "測試漏洞名2",
"漏洞等級": "嚴(yán)重"
},
{
"漏洞名": "測試漏洞名2",
"漏洞等級": "中危"
}
],
"漏洞詳情": [
{
"漏洞名": "測試漏洞名1",
"漏洞地址": "http://blog.gm7.org/",
"漏洞等級": "高危",
"漏洞危害": "危害XXX",
"復(fù)現(xiàn)過程": "先xxx,再xxx尖殃,最后xxx",
"修復(fù)建議": "更新到最新版本即可"
},
{
"漏洞名": "測試漏洞名2",
"漏洞地址": "http://bblog.gm7.org/",
"漏洞等級": "嚴(yán)重",
"漏洞危害": "危害XXX",
"復(fù)現(xiàn)過程": "先xxx丈莺,再xxx,最后xxx",
"修復(fù)建議": "更新到最新版本即可"
},
{
"漏洞名": "測試漏洞名3",
"漏洞地址": "http://cblog.gm7.org/",
"漏洞等級": "中危",
"漏洞危害": "危害XXX",
"復(fù)現(xiàn)過程": "先xxx送丰,再xxx缔俄,最后xxx",
"修復(fù)建議": "更新到最新版本即可"
}
]
}
doc.render(context)
doc.save("generated_doc.docx")
結(jié)果如下: