关于python:快速生成定制化的Word文档Python实践指南

1.1. 前言

家喻户晓,安服工程师又叫做Word工程师,在打工或者批量SRC的时候,如果产出很多,又须要一个一个的写报告的状况下会十分的折磨人,因而查了一些相干的材料,发现应用python的docxtpl库批量写报告成果很不错,记录一下。

1.2. 介绍

docxtpl 是一个用于生成 Microsoft Word 文档的模板引擎库,它联合了 docx 模块和 Jinja2 模板引擎,使用户可能应用 Microsoft Word 模板文件并在其中填充动态数据。它提供了一种不便的形式来生成个性化的 Word 文档,并反对条件语句、循环语句和变量等控制结构,以满足不同的文档生成需要。

官网GitHub地址:https://github.com/elapouya/python-docx-template

官网文档地址:https://docxtpl.readthedocs.io/en/latest/

简略来说:就是创立一个相似Jinja2语法的模板文档,而后往里面动静填充内容就能够了

装置:

pip3 install docxtpl

1.3. 根底应用

from docxtpl import DocxTemplate

doc = DocxTemplate("test.docx")
context = {'whoami': "d4m1ts"}
doc.render(context)
doc.save("generated_doc.docx")

其中,test.docx内容如下:

生成后的后果如下:

1.4. 案例介绍

1.4.1. 需要假如

写一份不思考好看的漏扫报告,须要有统计后果图和破绽详情,每个破绽包含破绽名、破绽地址、破绽等级、破绽危害、复现过程、修复倡议六个局部。

1.4.2. 模板文档筹备

编写的模板文档如下,应用到了常见的iffor赋值等,保留为template.docx,后续只须要向外面填充数据即可。

1.4.3. 数据结构剖析

传入数据须要一串json字符串,因而咱们依据模板文档梳理好json构造,而后传入即可。

梳理好的数据结构如下:

{
  "饼图": "111",
  "柱状图": "222",
  "破绽简报": [
    {
      "破绽名": "测试破绽名1",
      "破绽等级": "高危"
    }
  ],
  "破绽详情": [
    {
      "破绽名": "测试破绽名1",
      "破绽地址": "http://blog.gm7.org/",
      "破绽等级": "高危",
      "破绽危害": "危害XXX",
      "复现过程": "先xxx,再xxx,最初xxx",
      "修复倡议": "更新到最新版本即可"
    }
  ]
}

编写代码测试一下可行性:

from docxtpl import DocxTemplate

doc = DocxTemplate("template.docx")
context = {
      "饼图": "111",
      "柱状图": "222",
      "破绽简报": [
        {
          "破绽名": "测试破绽名1",
          "破绽等级": "高危"
        },
        {
          "破绽名": "测试破绽名2",
          "破绽等级": "重大"
        },
        {
          "破绽名": "测试破绽名2",
          "破绽等级": "中危"
        }
      ],
      "破绽详情": [
        {
          "破绽名": "测试破绽名1",
          "破绽地址": "http://blog.gm7.org/",
          "破绽等级": "高危",
          "破绽危害": "危害XXX",
          "复现过程": "先xxx,再xxx,最初xxx",
          "修复倡议": "更新到最新版本即可"
        },
        {
          "破绽名": "测试破绽名2",
          "破绽地址": "http://bblog.gm7.org/",
          "破绽等级": "重大",
          "破绽危害": "危害XXX",
          "复现过程": "先xxx,再xxx,最初xxx",
          "修复倡议": "更新到最新版本即可"
        },
        {
          "破绽名": "测试破绽名3",
          "破绽地址": "http://cblog.gm7.org/",
          "破绽等级": "中危",
          "破绽危害": "危害XXX",
          "复现过程": "先xxx,再xxx,最初xxx",
          "修复倡议": "更新到最新版本即可"
        }
      ]
    }

doc.render(context)
doc.save("generated_doc.docx")

很好,达到了预期的成果。

1.4.4. 退出图表

在下面的过程中,内容简直是没问题了,然而图表还是没有展现进去。生成图表咱们应用plotly这个库,并将生成内容写入ByteIO

相干代码如下:

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: 饼图题目
    :param labels: 饼图标签
    :param values:  饼图数据
    :param colors:  饼图每块的色彩
    :return:
    """
    # 根底饼图
    fig = go.Figure(data=[go.Pie(
        labels=labels,
        values=values,
        hole=.4,  # 核心环大小
        insidetextorientation="horizontal"
    )])
    # 更新色彩
    fig.update_traces(
        textposition='inside',  # 文本显示地位
        hoverinfo='label+percent',  # 悬停信息
        textinfo='label+percent', # 饼图中显示的信息
        textfont_size=15,
        marker=dict(colors=colors)
    )
    # 更新题目
    fig.update_layout(
        title={   # 设置整个题目的名称和地位
            "text": title,
            "y": 0.96,  # y轴数值
            "x": 0.5,  # x轴数值
            "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: 题目
    :param x: 柱状图标签
    :param y:  柱状图数据
    :return:
    """
    # x轴长度最为18
    b = x
    x = []
    for i in b:
        if len(i) >= 18:
            x.append(f"{i[:15]}...")
        else:
            x.append(i)

    # 根底柱状图
    fig = go.Figure(data=[go.Bar(
        x=x,
        y=y,
        text=y,
        textposition="outside",
        marker=dict(color=["#3498DB"] * len(y)),
        width=0.3
    )])
    # 更新题目
    fig.update_layout(
        title={  # 设置整个题目的名称和地位
            "text": title,
            "y": 0.96,  # y轴数值
            "x": 0.5,  # x轴数值
            "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. 最终后果

要插入图片内容,代码语法如下:

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: 饼图题目
    :param labels: 饼图标签
    :param values:  饼图数据
    :param colors:  饼图每块的色彩
    :return:
    """
    # 根底饼图
    fig = go.Figure(data=[go.Pie(
        labels=labels,
        values=values,
        hole=.4,  # 核心环大小
        insidetextorientation="horizontal"
    )])
    # 更新色彩
    fig.update_traces(
        textposition='inside',  # 文本显示地位
        hoverinfo='label+percent',  # 悬停信息
        textinfo='label+percent',  # 饼图中显示的信息
        textfont_size=15,
        marker=dict(colors=colors)
    )
    # 更新题目
    fig.update_layout(
        title={  # 设置整个题目的名称和地位
            "text": title,
            "y": 0.96,  # y轴数值
            "x": 0.5,  # x轴数值
            "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: 题目
    :param x: 柱状图标签
    :param y:  柱状图数据
    :return:
    """
    # x轴长度最为18
    b = x
    x = []
    for i in b:
        if len(i) >= 18:
            x.append(f"{i[:15]}...")
        else:
            x.append(i)

    # 根底柱状图
    fig = go.Figure(data=[go.Bar(
        x=x,
        y=y,
        text=y,
        textposition="outside",
        marker=dict(color=["#3498DB"] * len(y)),
        width=0.3
    )])
    # 更新题目
    fig.update_layout(
        title={  # 设置整个题目的名称和地位
            "text": title,
            "y": 0.96,  # y轴数值
            "x": 0.5,  # x轴数值
            "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="破绽数量",
        labels=["重大", "高危", "中危", "低危"],
        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",
            "破绽等级": "重大"
        },
        {
            "破绽名": "测试破绽名2",
            "破绽等级": "中危"
        }
    ],
    "破绽详情": [
        {
            "破绽名": "测试破绽名1",
            "破绽地址": "http://blog.gm7.org/",
            "破绽等级": "高危",
            "破绽危害": "危害XXX",
            "复现过程": "先xxx,再xxx,最初xxx",
            "修复倡议": "更新到最新版本即可"
        },
        {
            "破绽名": "测试破绽名2",
            "破绽地址": "http://bblog.gm7.org/",
            "破绽等级": "重大",
            "破绽危害": "危害XXX",
            "复现过程": "先xxx,再xxx,最初xxx",
            "修复倡议": "更新到最新版本即可"
        },
        {
            "破绽名": "测试破绽名3",
            "破绽地址": "http://cblog.gm7.org/",
            "破绽等级": "中危",
            "破绽危害": "危害XXX",
            "复现过程": "先xxx,再xxx,最初xxx",
            "修复倡议": "更新到最新版本即可"
        }
    ]
}

doc.render(context)
doc.save("generated_doc.docx")

后果如下:

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理