freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

安服仔偷懒必备技能之word批量处理脚本
2023-04-23 14:55:07
所属地 广东省

前言

作为一个有理想的安服仔,肯定是担任重要任务,很多事情都要解决,虽然简单但是很繁琐,特别是word的文档处理这块,经常要处理几十个文档就很烦,挣扎了下还是打算写个脚本来批量处理这些事情,总不能因为这些事情耽误了自己进步(摸鱼)吧?由于之前没接触过py在word文档处理这块的东西,所以还是踩了一些坑的,写一下,后续有用到可以避避坑。

前期准备

老样子,一开始还是先思考下,如果正常操作,我们会如何操作,然后根据这些操作去构思我们的路线,所有的方便都不会是直接来的,不是一两个函数就能解决的事情,就好像3D打印,你没构图,3D打印也没办法打出你想要的东西出来,所以一开始我构思了三条路线:

一:直接定位里面的内容,然后删除,插入自己想要的;

二:把我想要的内容独立做成一个文档,然后定位内容,把老的删除,删除之后粘贴新的;

三:将原文档的内容进行切块,然后再把自己想要的内容独立成一些文档,然后再把每个文档进行拼接,得到新的文档;

其实还有其他的思路,但是有点太过繁琐,后面我在实现的过程中发现实际上不需要那么复杂,是自己想太多了,后面会谈到,那么先说下第一个思路,简单易懂对吧,但是具体实现起来,发现删除了之后,我重新写进去的数据,会丢失之前的格式,而py的docx对于格式方面虽然说可以赋予格式,但是没办法跟真正的word一样去设置每一部分的格式,而且有一些是不存在的,所以这块就十分的麻烦,如果你的文档不存在格式的问题,就简简单单的文字,那完全可以根据这个思路去做,而且很方便。第二个思路的话,有个很重要的点就是,定位可以定位,但是粘贴新的内容的时候,他会跟在文档末尾,也就是说,如果你想要粘贴的在文档中间,那没办法去直接粘贴到指定位置(PS:没怎么接触这个,我就只是为了实现这个东西,才去看了下,如果有话,是小弟太菜了),第三个思路的话,乍一看似乎可以得到我们想要的东西,但是如果文件量多起来的话,问题在于怎么去做到准确切割自己想要的东西。

实现过程

多说无益,动手才知道坑在哪,如果要批量转化word的话,那么第一步肯定是先拿到我们文件夹里面的文件,然后再轮询,这个可以先不管,先试试能不能修改word里面的内容,ok,这里经过一系列的百度之后,我发现了python的一个很神奇的库—python-docx,用于创建可修改微软Word的一个python库,提供全套的 Word操作,是最常用的Word工具,而且能保留我们原有的格式,那这不是直接起飞???

在开始使用之前,先说下关于word的一些概念:

  • Document:是一个 Word 文档 对象,不同于 VBA 中 Worksheet 的概念,Document 是独立的,打开不同的 Word 文档,就会有不同的 Document 对象,相互之间没有影响
  • Paragraph:是段落,一个 Word 文档由多个段落组成,当在文档中输入一个回车键,就会成为新的段落,输入 shift + 回车,不会分段
  • Run表示一个节段,每个段落由多个 节段 组成,一个段落中具有相同样式的连续文本,组成一个节段,所以一个 段落 对象有个 Run 列表

也就是说word 文档内容的结构是这样划分的:

1682232635_6444d53b3053ea4d01b9e.png!small

第二个 段落(paragraph),没有内容,所以 节段(run)为空,了解到这,也基本上知道如何去定位我们想要的数据了,先轮询paragraph,然后在这个paragraph里面去轮询run,从而拿到我们的要的数据,说干就干,直接上,就拿上面的概念为文本,我们写个代码块去遍历这些段落,然后输出出来

1
2
3
4
5
6
from docx import Document

tpl_doc='title.docx'
doc = Document(tpl_doc)
for paragraph in doc.paragraphs:
print(paragraph.text)

输出结果是这样的:

1682232651_6444d54b08c61f554ca51.png!small

这就是每一段的内容,再遍历每一段里面的run块出来

1682232663_6444d5578a821d7f13e27.png!small

可以看到run的区分不是根据词语的,而是根据写入的时间节点,如果时间节点不一样,那么run的内容就不一样,同一段文字,可能写入的时间节点不一致,遍历run的时候结果也不一样,这点暂时没办法解决,目前能想到的就是遍历的时候组合文字,当匹配出自己想要的文之后再进行替换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
params = {
"原文RUN-A": "替换的文字",
"原文RUN-B": "替换的文字",
"原文RUN-C": "替换的文字",
"原文RUN-D": "替换的文字",
}
for paragraph in doc.paragraphs:
for param in params:
pv = str(params[param])
ph = f'{param}'
if ph in paragraph.text:
for run in paragraph.runs:
if ph in run.text:
run.text = run.text.replace(ph, pv)
run.italic = False

这些能有效处理开头和中间的文字,而且这种方式替换的话,不会去掉文章原有的格式,而如果有需要替换大量的文字段落在文章末的时候,可以将文末需要替换的文字删除,需要删除空行,普通的clean命令只是将内容删除,而不会去除换行,所以删除之后会存在空行的情况 ,这时候如果复制另外一个文档的内容接入到文末的话,他会接在空行后面,所以需要删除空行。

1
2
3
4
5
6
for paragraph in doc.paragraphs:
if(paragraph.text=="段落内容"):
p = paragraph._element
p.getparent().remove(p)
p._p = p._element = None
#这部分是遍历段落,将文末需要替换的段落进行删除去除空行,方便后续将另一个文件的内容复制进来

这里有一点就是,把其他文档里面的内容合并到这个文档的时候,其他文档里面的文字内容格式是不会变的,这也跟我们最初的想法一致,保留原有的格式,所以可以直接使用

1
2
3
4
5
6
7
8
bottom_title = 'bottom.docx'
bottom_document = Document(bottom_title)
master = Document('old_docx.docx')
middle_new_docx = Composer(master)
middle_new_docx.append(bottom_document)
filename = old_docx.docx
print(filename)
middle_new_docx.save(filename)

到这基本需要替换的就能够替换掉,并且保留文档格式了,但是有一点就是,去除空行不能够遍历全文,最好就是先定位到位置,再增加判断条件,不然容易将图片内容删除,因为当你遍历段落的时候,图片独占一行的时候该段落是空的,所以会将图片给删除掉,可以额外加一个判断,当存在图片的时候,该段落的run值是为0,而不存在图片单纯一个空行的时候,run是不存在任何值的。

# 工具使用
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者