学点python,自动化生成word报告

内容分享4小时前发布
0 3 0

学点python,自动化生成word报告

  

自动化处理Excel文件Pandas可是当之无愧的利器,那么有没有能自动化处理Word的神器呢?答案是有,本文将介绍如何使用python-docx库来处理Word文档,但是个人感觉它不能算上处理Word的神器,反而是一种不得已的选择,这可能也跟Word文档的数据结构化程度比Excel要复杂得多的缘故。

如果完全使用python-docx来生成完整的Word文档,要实现一些复杂样式会相当复杂,今天我就通过一个具体的示例来讲解一种折中的方案,那就是通过事先编写好的模板文件,仅通过更新模板文件中的定义好的占位符来生成Word文档。这样的方式可以最大限度地利用模板文件的样式,只需要学习几个python-docx简单的API就可以做到自动化生成美丽报告,对于需要常常处理Word文档的工作者而言,可以让你从繁琐的键盘敲击中解放出来。使用python-docx前需要先进行安装。

pip install python-docx

为了方便演示,创建一个如下图所示的简单模板示例,我们的目标是编写python程序自动替换模板中的由{}包裹的内容,并生成样式和模板一致的Word报告。标题中的{report_date}将替换为报告的日期;正文段落的{num_xxx}表明将替换为数字,并设置为标红加粗的样式;{image_demo}表明这里将插入图片;最后表格部分仅保留表头,示例将第一列表头的内容作为识别要更新表格的标识,这里为NO.。

学点python,自动化生成word报告

以上只是本次约定的规则,实际应用中你可以创建专业的模板并自行定义占位符的规则。

接着简单介绍python-docx的基本概念,让我们对它有个基本的理解,后面就直接通过具体的示例代码展示如何使用它们,这里完全不会用到其复杂的内容。

1. Document对象,对应的就是Word文档,打开或新建文档都使用Document方法。

2. 我们知道Word文档是由标题、段落、节、表格等元素组成,python-docx也都有对应的概念,它们是可迭代对象,在程序中可以遍历文档对象的段落、表格进行相应的处理操作。

3. Run对象,列如示例模板中的数字可以单独标红加粗,设置不同的样式,它们就是一个Run对象,简单理解,就是一个段落中的小分块,每个分块可以单独设置样式。

4. save方法将更新的Document保存为Word文档。

下面就直接给出代码,大部分代码都进行了注释,示例代码将模板文件命名为template.docx,另外还需要事先准备一个名为demo.png的图片放到和代码同一目录下。

import re
import time
from docx import Document
from docx.shared import Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
def format_number_red_bold(paragraph):
    """
    将段落中的数字标红加粗
    整体的思路是把段落重新拆分为文本和数字,然后重新创建新的Run对象,针对数字的样式进行修改
    """
    # 正则表达式匹配数字
    pattern = re.compile(r'd+')
    # 记录拆分的段落文本和数字Run对象
    new_runs = []
    # 当前处理的文本起始位置
    start = 0


    # 找到所有数字的位置
    for match in pattern.finditer(paragraph.text):
        number_start, number_end = match.span()
        # 如果数字前面有文本,添加一个新的Run对象保存数字前的文本
        if number_start > start:
            text = paragraph.text[start:number_start]
            new_runs.append((text, "text"))
        # 创建包含数字的Run对象,并设置样式
        text = paragraph.text[number_start:number_end]
        new_runs.append((text, "digit"))
        # 更新当前处理的文本起始位置
        start = number_end
    # 段落末尾的文本
    new_runs.append((paragraph.text[start:], "text"))
    # 清空段落
    paragraph.text = ""
    # 重新组装段落并将数字的样式改为标红加粗
    for text, style in new_runs:
        run = paragraph.add_run(text)
        if style == "digit":
            run.font.color.rgb = RGBColor(255, 0, 0)  # 红色
            run.font.bold = True  # 加粗
def generate_report_from_template(template_path, output_path, report_data):
    """
    根据提供的数据更新Word模板并生成报告文件
    :param template_path: 模板文件
    :param output_path: 输出报告文件
    :param report_data: 包含要更新的数据字典,
    """
    # 打开模板文档
    doc = Document(template_path)
    # 遍历模板文档中的段落
    for para in doc.paragraphs:
        # 遍历检查段落中是否有需要替换的占位符
        for placeholder, new_text in report_data.items():
            # 排除图片占位符
            if not placeholder.startswith('image_'):
                # 如果当前段落文本包含占位符,则替换为占位符对应的文本
                if f'{{{placeholder}}}' in para.text:
                    para.text = para.text.replace(f'{{{placeholder}}}', new_text)


    # 段落被占位符替换后样式会丢失,遍历段落设置数字的样式为标红加粗
    for para in doc.paragraphs:
        # 判断是否是需要对数字标红加粗的段落
        if para.text.startswith("这是一段包含数字的段落示例"):
            # 重新将需要标红加粗的数字进行样式修改
            format_number_red_bold(para)


    # 更新报告表格数据
    for table in doc.tables:
        # 获取表格第一行第一列标题,作为判定表格的依据
        th0 = table.rows[0].cells[0].text
        # 遍历报告数据中的数据
        for thead, tdata in report_data.items():
            # 如果第一列标题和报告数据中的key一样,则表明该表格需要更新
            if th0 == thead:
                # 记录新增表格的行
                new_added_rows = []
                for _ in tdata:
                    new_added_rows.append(table.add_row())
                # 遍历新增的行
                for index, new_row in enumerate(new_added_rows):
                    # 遍历每一行的单元格
                    for cell_idx, cell in enumerate(new_row.cells):
                        # 取表格对应的数据项设置为单元格的文本
                        cell.text = tdata[index][cell_idx]
                        # 设置单元格样式为居中
                        for p in cell.paragraphs:
                            p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
    # 更新报告图片
    for para in doc.paragraphs:
        # 遍历数据字典
        for placeholder, image_path in report_data.items():
            # 判断是否是图片占位符
            if placeholder.startswith("image_"):
                if f'{{{placeholder}}}' in para.text:
                    # 占位符所在的段落清除并添加图片
                    para.clear()
                    run = para.add_run()
                    # Inches(5)用于调整图片的大小比例
                    run.add_picture(image_path, width=Inches(5))
                    # 设置图片居中
                    para.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER  


    # 保存更新后的文档
    doc.save(output_path)
if __name__ == "__main__":
    # 模板文件路径
    template_path = "./template.docx"
    # 指定生成word报告路径
    output_path = "report.docx"
    # 模拟报告数据
    report_data = {
        "report_date": time.strftime("%Y/%m/%d"),
        "num_001": f"{123}",
        "num_002": f"{456}",
        "num_003": f"{789}",
        "image_demo": "demo.png",
        "NO.": [
            ("001", "key01", "val01"),
            ("002", "key02", "val02"),
            ("003", "key03", "val03"),
        ]
    }
    # 根据模板生成word报告
    generate_report_from_template(template_path, output_path, report_data)

运行代码将会在一样目录下生成名为report.docx的Word文档,打开内容将如下图所示。

学点python,自动化生成word报告

参考文献:

[1].https://python-docx.readthedocs.io/en/latest/index.html#api-documentation

© 版权声明

相关文章

3 条评论

  • 头像
    荒唐俊 读者

    如果Python搞这么复杂还不如手写进去,还请用普通用户思维10条代码搞定,我一定点赞,程序员思维就算了,因为你们是专业的编程高手

    无记录
    回复
  • 头像
    大地无言行者无疆 读者

    但你忽略了一个问题,人工输入的话,简单的,并且是几十份还好,如果复杂的,大文档,成千上万份,你确定你还会说,用手工输入吗?程序的复杂,不是让你每次都要去编写,而是一次性写好,然后去匹配大多数场景。。

    无记录
    回复
  • 头像
    欣欣-YIBO 投稿者

    收藏了,感谢分享

    无记录
    回复