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 」即可获得全部源码

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

推荐阅读

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

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

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

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

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

最全总结 | 聊聊 Python 办公自动化之 Word(下)的更多相关文章

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

    1. 前言 上一篇文章,对 Word 写入数据的一些常见操作进行了总结 最全总结 | 聊聊 Python 办公自动化之 Word(上) 相比写入数据,读取数据同样很实用! 本篇文章,将谈谈如何全面读取 ...

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

    1. 前言 日常自动化办公中,使用 Python 真的能做到事半功倍! 在上一个系列中,我们对 Python 操作 Excel 进行了一次全面总结 最全总结 | 聊聊 Python 办公自动化之 Ex ...

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

    1. 前言 自动化办公,非 Python 莫属! 从本篇文章开始,我们继续聊聊自动化办公中另外一个常用系列:PPT 2. 准备一下 Python 操作 PPT 最强大的依赖库是:python-pptx ...

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

    1. 前言 上一篇文章简单地介绍了 PPT 的文档结构,并使用 python-pptx 这个依赖库完成对 PPT 文档最基本的操作 最全总结 | 聊聊 Python 办公自动化之 PPT(上) 作为 ...

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

    1. 前言 上一篇文章中,我们聊到使用 xlrd.xlwt.xlutils 这一组合操作 Excel 的方法 最全总结 | 聊聊 Python 办公自动化之 Excel(上) ​本篇文章将继续聊另外一 ...

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

    1. 前言 前面谈到 Python 处理 Excel 文件最常见的两种方式,即:xlrd/xlwt.openpyxl ​其中, xlrd/xlwt 这一组合,xlrd 可以负责读取数据,而 xlwt ...

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

    1. 前言 在我们日常工作中,经常会使用 Word.Excel.PPT.PDF 等办公软件 但是,经常会遇到一些重复繁琐的事情,这时候手工操作显得效率极其低下:通过 Python 实现办公自动化变的很 ...

  8. 最全总结 | 聊聊 Python 数据处理全家桶(PgSQL篇)

    1. 前言 大家好,我是安果! Python 数据处理全家桶,截止到现在,一共写过 6 篇文章,有兴趣的小伙伴可以去了解一下! 最全总结 | 聊聊 Python 数据处理全家桶(Mysql 篇) 最全 ...

  9. 最全总结 | 聊聊 Python 数据处理全家桶(Mysql 篇)

    1. 前言 在爬虫.自动化.数据分析.软件测试.Web 等日常操作中,除 JSON.YAML.XML 外,还有一些数据经常会用到,比如:Mysql.Sqlite.Redis.MongoDB.Memch ...

随机推荐

  1. 在WPF中一种较好的绑定Enums数据方法

    引言 在你使用wpf应用程序开发的时候,是否需要进行数据绑定到Enum数据呢?在这篇文章中,我将向你展示在WPF中处理Enum数据绑定的方法. 假设存在一个这样的Enum数据的定义,具体内容如下文代码 ...

  2. Linux系统Yum仓库制作

    在使用Linux系统的时候,通常需要安装许多软件,Linux系统通常安装软件有源码包安装(文件格式:.tar.gz 或.tar.bz2:安装过程:解压.环境检查.编译和安装).Rpm包安装(文件格式: ...

  3. Django model总结(上)

    Django model是django框架中处于比较核心的一个部位,准备分三个博客从不同的方面分别进行阐述,本文为<上篇>,主要对[a]Model的基本流程,比如它的创建,迁移等:默认行为 ...

  4. Altium Designer中如何批量修改元器件封装?

    我想你说的应该是altium里的封装管理库吧.1,Tools -> Footprint Manager -> ...2,在Component List里选择要改的器件3,在View and ...

  5. git的远程分支是干啥的,和本地的有什么区别?

    不知道大家有没有经历过,当我们切换到了一个新的分支想要提交代码的时候,总会遇到这样的错误. 我们把日志里的英文翻译过来是说,我们当前的分支没有设置任何上游分支.然后git提示我们可以运行下面这行代码来 ...

  6. Luogu P5307 [COCI2019] Mobitel

    题意 有一个 \(r\times c\) 的矩阵 \(a\),矩阵的每个位置都有一个正整数,求从左上角走到右下角并且满足路径上数字乘积之和大于 \(n\) 的方案数. \(\texttt{Data R ...

  7. Netty源码解析 -- 服务端启动过程

    本文通过阅读Netty源码,解析Netty服务端启动过程. 源码分析基于Netty 4.1 Netty是一个高性能的网络通信框架,支持NIO,OIO等多种IO模式.通常,我们都是使用NIO模式,该系列 ...

  8. Vue+Antd搭配百度地图实现搜索定位等功能

    前言 最近,在做vue项目的时候有做到选择地址功能,而原项目中又引入了百度地图,所以我就打算通过使用百度地图来实现地址搜索功能啦. 本次教程可能过于啰嗦,所以这里先放上预览地址供大家预览--点我预览, ...

  9. 正式班D21

    2020.11.03星期二 正式班D21 目录 11.5 源码包 11.5.1 预先安装编译安装依赖的库 11.5.2 官网下载源码包 11.5.3 解压.编译.编译安装 11.5 源码包 11.5. ...

  10. Serilog 源码解析——Sink 的实现

    在上一篇中,我们简单地查看了 Serilog 的整体需求和大体结构.从这一篇开始,本文开始涉及 Serilog 内的相关实现,着重解决第一个问题,即 Serilog 向哪里写入日志数据的.(系列目录) ...