11

最全总结 | 聊聊 Python 办公自动化之 Word(下)

 3 years ago
source link: http://www.cnblogs.com/xingag/p/14038340.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

bq6JFva.jpg!mobile

1. 前言

关于 Word 文档的读写,前面两篇文章分别进行了一次全面的总结

最全总结 | 聊聊 Python 办公自动化之 Word(上)

最全总结 | 聊聊 Python 办公自动化之 Word(中)

本篇文章作为一个办公自动化 Word 篇的一个补充,写写几个比较实用的办公场景

包含:

  • 页眉页脚处理

  • 合并多个文档

  • 新增数字索引

  • doc 批量转 docx

  • 对比文档差异性

  • 特别内容标注

  • 替换文字内容

2. 页眉页脚

每一个页面章节都包含:页眉页脚

它可以单独设置,每个页面都不一样;也可以全部设置成与首页一样

这个功能,由章节对象中的属性 different_first_page_header_footer 来控制

  • 当值为 True 时,代表页眉页脚不同于首页,每个页面章节的页眉、页脚都可以单独设置

  • 当值为 False 时,所有页面的页眉、页脚都一样

# 1、获取待处理页眉、页脚的章节
header = self.doc.sections[0].header
footer = self.doc.sections[0].footer

# True if this section displays a distinct first-page header and footer
# True:页眉页脚不同于首页,每个页面章节的页眉页脚单独设置
# False:每个页面的页眉页脚相同
self.doc.sections[0].different_first_page_header_footer = True

添加页眉页脚包含两种,分别是:普通页眉页脚、自定义样式的页眉页脚

1 - 普通页眉页脚

def add_norm_header_and_footer(header, footer, header_content, footer_content):
    """
    增加一个普通的页眉、页脚,并居中显示
    :param header_content:
    :param footer_content:
    :return:
    """
    # 新增/修改页眉、页脚
    # 注意:一般页眉、页脚里只有一个段落
    header.paragraphs[0].text = header_content
    footer.paragraphs[0].text = footer_content

    # 居中显示
    header.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
    footer.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

# 2、新增页眉
# 2.1 普通的页眉、页脚
add_norm_header_and_footer(header, footer, "我是一个页眉", "我是一个页脚")
2 - 自带样式的页眉页脚

2 - 自带样式的页眉页脚

def add_custom_style_header_and_footer(header, footer, header_content, footer_content, style):
    """
    新增自定义的页眉、页脚
    :param header:
    :param footer:
    :param header_content:
    :param footer_content:
    :param style:
    :return:
    """
    # 注意:style_type=2,否则会报错
    header.paragraphs[0].add_run(header_content, style)
    footer.paragraphs[0].add_run(footer_content, style)

# 2.2 自带样式的页眉、页脚
# 创建一个样式
style_paragraph = create_style(document=self.doc, style_name="style5", style_type=2, font_size=30,
                               font_color=[0xff, 0x00, 0x00], align=WD_PARAGRAPH_ALIGNMENT.CENTER)
add_custom_style_header_and_footer(header, footer, "我是页眉2", "我是页脚2", style_paragraph)

如果想将文档中所有的页眉、页脚删除掉,只需要 2 个步骤:

  • 遍历文档中所有页面章节,将其 different_first_page_header_footer 属性值设置为 False

  • 设置章节对象页眉页脚的 is_linked_to_previous 属性值为 True

    PS:当 is_linked_to_previous 设置为 True 时,页眉页脚会被删除

def remove_all_header_and_footer(doc):
    """
    删除文档中所有页眉和页脚
    :param doc:
    :return:
    """
    for section in doc.sections:
        section.different_first_page_header_footer = False
        # 当is_linked_to_previous设置为True时,页眉页脚会被删除
        section.header.is_linked_to_previous = True
        section.footer.is_linked_to_previous = True

3. 合并多个文档

日常工作中,经常会遇到将多个 Word 文档合并成一个文件的需求

这里,可以使用另外一个 Python 依赖库:docxcompose

# 合并多个文件的依赖库
pip3 install docxcompose

使用也非常简单,只需要下面 4 行代码,就能将多个文件进行合并,生成到一个新的文件中去

from docxcompose.composer import Composer

def compose_files(self, files, output_file_path):
    """
    合并多个word文件到一个文件中
    :param files:待合并文件的列表
    :param output_file_path 新的文件路径
    :return:
    """
    composer = Composer(Document())
    for file in files:
        composer.append(Document(file))

    # 保存到新的文件中
    composer.save(output_file_path)

4. 新增数字索引

我们经常需要在文档页脚处添加页面数字索引,可惜 python-docx 并没有提供现有方法

但是,在 stackoverflow 上找到实现的方式

https://stackoverflow.com/questions/56658872/add-page-number-using-python-docx?rq=1

from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne, ZeroOrMore, OxmlElement
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.oxml import ns

def create_element(self, name):
    return OxmlElement(name)

def create_attribute(self, element, name, value):
    element.set(ns.qn(name), value)

def add_page_number(self, run):
    """
    添加页面索引
    :param run:
    :return:
    """
    fldChar1 = self.create_element('w:fldChar')
    self.create_attribute(fldChar1, 'w:fldCharType', 'begin')

    instrText = self.create_element('w:instrText')
    self.create_attribute(instrText, 'xml:space', 'preserve')
    instrText.text = "PAGE"

    fldChar2 = self.create_element('w:fldChar')
    self.create_attribute(fldChar2, 'w:fldCharType', 'end')

    # run._r:class 'docx.oxml.text.run.CT_R'>
    run._r.append(fldChar1)
    run._r.append(instrText)
    run._r.append(fldChar2)

默认生成的数字索引在页脚左下角,并不美观!

因此,这里我们可以使用 第一篇文章 的方法创建一个「文字块样式」,然后以文字块 Run 的形式,添加到页脚的第一个段落中去

# 注意:要设置页眉页脚的对齐方式,必须设置到段落上(文字块不能添加对齐方式)
doc.sections[0].footer.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

# 创建一个文字块样式,指定字体名称、大小、颜色
style = create_style(document=doc, style_name="style", style_type=2, font_size=10,
                     font_color=[0x00, 0x00, 0x00], font_name="黑体")
self.add_page_number(doc.sections[0].footer.paragraphs[0].add_run("", style))
doc.save("./output.docx")
print('添加页码索引成功!')

需要注意的,如果需要设置页面数字索引的对齐方式,必须针对页脚的段落进行设置,修改其 alignment 属性值即可

5. doc 转 docx

python-docx 对 doc 格式的文档不太友好,要处理这类文档,我们需要先将它转换为 docx 格式

对于 Windows 系统,完全可以使用 win32com 这个模块,用命令去调用 Word 应用,打开源文件后,保存了 docx 格式的文件即可

from win32com import client

def doc_to_docx_in_win(path_raw, path_output):
​    """
    doc转为docx(win)
    :param path_original:
    :param path_final:
    :return:
    """
    # 获取文件的格式后缀
    file_suffix = os.path.splitext(path_raw)[1]
    if file_suffix == ".doc":
        word = client.Dispatch('Word.Application')
        # 源文件
        doc = word.Documents.Open(path_raw)
        # 生成的新文件
        doc.SaveAs(path_output, 16)
        doc.Close()
        word.Quit()
    elif file_suffix == ".docx":
        shutil.copy(path_raw, path_output)

而对于 Mac/Linux,推荐使用 LibreOffice 去转换文档格式

# 转换格式
./soffice --headless --convert-to docx 源文件.doc --outdir /output/path/

PS:LibreOffice 是一款由社区创造的自由免费办公套件,跨平台,内置的 soffice 可以用于文件转换

以 Mac OS 为例,我们按下面步骤来操作

  • 官网下载 LibreOffice 软件并安装

  • 找到 LibreOffice 软件安装目录,将 soffice 命令所在目录配置到环境变量中

  • 重启 Pycharm

  • 使用 os 模块下的 walk() 函数遍历所有源文件,组成一条 soffice 转换命令

  • 执行转换命令

import os

source = "./doc/"
dest = "./docx/"
g = os.walk(source)

# 遍历文件夹
for root, dirs, files in g:
    for file in files:
        # 源文件完整路径
        file_path_raw = os.path.join(root, file)
        print(file_path_raw)

        os.system("soffice --headless --convert-to docx {} --outdir {}".format(file_path_raw, dest))

6. 对比文档差异性

两个 Word 文档的对比也是工作中比较常见的需求了

首先,遍历文档中所有段落,过滤掉空行,获取所有文本内容

# 分别获取段落内容
content1 = ''
content2 = ''
for paragraph in file1.paragraphs:
​    if "" == paragraph.text.strip():
        continue
    content1 += paragraph.text + '\n'

for paragraph in file2.paragraphs:
    if "" == paragraph.text.strip():
        continue
    content2 += paragraph.text + '\n'

# 如果参数 keepends 为 False,不包含换行符,如果为 True,则保留换行符。
print("第二个文档数据如下:\n", content1.splitlines(keepends=False))
print("第一个文档数据如下:\n", content1.splitlines(keepends=False))

接着,使用 Python 中的标准依赖库 difflib 对比文字间的差异,最后生成 HTML 差异报告

import codecs
from difflib import HtmlDiff

# 差异内容
diff_html = HtmlDiff(wrapcolumn=100).make_file(content1.split("\n"), content2.split("\n"))

# 写入到文件中
with codecs.open('./diff_result.html', 'w', encoding='utf-8') as f:
     f.write(diff_html)

7. 特别内容标注

我们经常需要对文档中部分重要内容进行特别标注

比如,我们需要对文档中包含「 微信 」的文字块或单元格,标为红色并加粗显示

1 - 段落内容

只需要遍历出段落中所有文字块 Run,直接修改文字块的 Font 属性即可

doc = Document(file)

# 关键字的文字块或单元格标红,并加粗
# 1、修改段落中包含关键字的文件块的样式
for paragraph in doc.paragraphs:
    for run in paragraph.runs:
        if keyword in run.text:
            # 修改颜色为红色,并加粗显示
            run.font.bold = True
            run.font.color.rgb = RGBColor(255, 0, 0)

2 - 表格内容

设置满足条件的单元格样式有点特别,需要经过下面 4 个步骤

  • 获取单元格对象,获取单元格文本内容,并临时保存

  • 清空单元格数据

  • 单元格对象追加一个段落和一个文字块 Run,返回一个文字块对象

  • 设置文字块对象样式,标红并加粗

tables = [table for table in doc.tables]
for table in tables:
    for row in table.rows:
        for cell in row.cells:
            if keyword in cell.text:
                # 原内容
                content_raw = cell.text
                # 清空单元格数据
                cell.text = ""
                # 追加数据进去,并设置样式
                run = cell.paragraphs[0].add_run(content_raw)
                run.font.color.rgb = RGBColor(255, 0, 0)
                run.font.bold = True

8. 替换文字内容

有时候,我们需要将文档中某个关键字全部替换成一个新的内容

这时候,我们可以遍历所有段落和表格,使用 replace() 函数对段落文本和单元格内容进行替换

def replace_content(self, old_content, new_content):
​    """
    替换文档中所有内容
    :param old_content:旧的内容
    :param new_content:新的内容
    :return:
    """
    # 替换段落
    for paragraph in self.doc.paragraphs:
        if old_content in paragraph.text:
            # 替换内容后,重新设置进去
            paragraph.text = paragraph.text.replace(old_content, new_content)

    # 替换表格
    # document.tables[表格索引].rows[行索引].cells[单元格列索引].text = “新的数据”。
    tables = [table for table in self.doc.tables]
    for table in tables:
        for row in table.rows:
            for cell in row.cells:
                if old_content in cell.text:
                    # 重新设置单元格内容
                    cell.text = cell.text.replace(old_content, new_content)

    # 保存到一个新的文件中
    self.doc.save('./new.docx')

9. 最后

到此,Python 自动化 Word 篇的内容全部结束了!

如果实际工作中,有一些其他的业务场景文中没有覆盖到,可以在文末进行留言,后面办公自动化实战篇可能会提供对应的解决方案!

要获取全部源码,关注公众号「 AirPython 」,后台回复「  word 」即可获得全部源码

如果你觉得文章还不错,请大家 点赞、分享、留言 下,因为这将是我持续输出更多优质文章的最强动力!


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK