快速生成定制化的Word文檔:Python實踐指南

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)容如下:

test.docx

生成后的結(jié)果如下:

gen

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ù)即可。

template

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ù)期的效果懈玻。

res

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é)果如下:

result
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市器躏,隨后出現(xiàn)的幾起案子俐载,更是在濱河造成了極大的恐慌,老刑警劉巖登失,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遏佣,死亡現(xiàn)場離奇詭異,居然都是意外死亡揽浙,警方通過查閱死者的電腦和手機贼急,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捏萍,“玉大人太抓,你說我怎么就攤上這事×铊荆” “怎么了走敌?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逗噩。 經(jīng)常有香客問我掉丽,道長,這世上最難降的妖魔是什么异雁? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任捶障,我火速辦了婚禮,結(jié)果婚禮上纲刀,老公的妹妹穿的比我還像新娘项炼。我一直安慰自己,他們只是感情好示绊,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布锭部。 她就那樣靜靜地躺著,像睡著了一般面褐。 火紅的嫁衣襯著肌膚如雪拌禾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天展哭,我揣著相機與錄音湃窍,去河邊找鬼闻蛀。 笑死,一個胖子當(dāng)著我的面吹牛您市,可吹牛的內(nèi)容都是我干的循榆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼墨坚,長吁一口氣:“原來是場噩夢啊……” “哼秧饮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起泽篮,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盗尸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帽撑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泼各,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年亏拉,在試婚紗的時候發(fā)現(xiàn)自己被綠了扣蜻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡及塘,死狀恐怖莽使,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笙僚,我是刑警寧澤芳肌,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站肋层,受9級特大地震影響亿笤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜栋猖,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一净薛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蒲拉,春花似錦肃拜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辱姨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間戚嗅,已是汗流浹背雨涛。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工枢舶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人替久。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓凉泄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蚯根。 傳聞我的和親對象是個殘疾皇子后众,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容