最近有个需求,需要在网站上增加一个功能,当管理员点击这个按钮时,程序会前往某个网站(以下简称A)以会员身份登录下载一份word(doc格式)的数据,当然,由于这是A网站提供的数据,肯定会有其水印,关键字,网站图片等,我们要做的就是讲这些内容去除掉,并加上自己的水印。
先吐槽一下百度,真是百度一下,你就学废,10个答案,8个一模一样,连错别字都没改,还有两个是广告。
首先,有个小知识你应该知道,当你把一份docx后缀的文件修改成.zip后缀文件时,你可以看到这份word文档的整体结构,如下图所示
而当你进入到word目录下面,你可以看到该docx文件内部的一些样式及图片,docx文件当中的图片都保存在media目录中,而我们word文档中的所有文本内容在document.xml文件中
如果我们只需要修改一份word文档的话,故事到这里就结束了,然鹅,我们毕竟是要做成批量自动化处理的,所以还得往下做。我们将document.xml文件以文本形式打开后,可以看到如下
如果将其丢在浏览器当中,我们可以看到他的结构,大致如下
插个题外话,XML是什么?官方介绍如下:XML 指可扩展标记语言(EXtensible Markup Language)。XML 是一种很像HTML的标记语言。XML 的设计宗旨是传输数据,而不是显示数据。XML 标签没有被预定义。您需要自行定义标签。XML 被设计为具有自我描述性。XML 是 W3C 的推荐标准。
我现在面临着两种解决方案,一种是直接修改document.xml文件的内容并存入,即生成的word样式全部跟A站保持一致,仅仅是修改内容;另外一种是将XML文件的内容完全解析出来,然后生成的word样式完全由我们自己来写(之前在处理word文档中写过一些简单的word生成介绍,其实python可以做的还可以更多,例如某些文字增加外链,控制部分内容的行距,缩进,字体大小,颜色,表格内容等)。
这两种方法不评价好坏,我只说处理方法,如果采取第一种方案,我们将xml文件使用with open打开,再在里面取出每一行的文本,并对其进行判断,如果包含A站关键词,就将其替换,这种相对简单;第二种方案是利用xml.dom.minidom的parse库来处理,代码如下:
from xml.dom.minidom import parsedomTree = parse("document.xml")# 文档根元素rootNode = domTree.documentElementt_texts = rootNode.getElementsByTagName("w:t") #所有我想要的文本内容均在w:t标签内部for t in t_texts: print(t.childNodes[0].data) #打印出所有的文本内容
大致的难点主要在上面,现在来记录一下整体的实现,由于拿到的是doc文件,我们是无法对其进行zip压缩获取结构,所以首先我们需要将其转化成docx文件(千万不要直接修改后缀!人工修改后缀名确实可以打开docx文件,但是你再进行修改后缀zip还是会报错),这里需要用到win32com模块,需要pip安装:
from win32com import client as cli #导入模块word = cli.Dispatch("Word.Application") # 打开word应用程序base_dir = #绝对路径地址file = base_dir + "test.doc"doc = word.Documents.Open(file) #打开word文件doc.SaveAs(base_dir + "output-file.docx", 12)#另存为后缀为".docx"的文件,其中参数12指docx文件doc.Close() #关闭原来word文件word.Quit() #千万不能少,否则程序会打不开你的程序,因为你的文档一直是打开的
然后再利用os的rename模块修改后缀名为test.zip,通过zipfile模块提取相关文件或者直接将其解压到一个文件夹内,再利用shutil模块的copy方法将document.xml文件复制出来(前面的第一种方案),对其进行修改之后,再利用copyfile方法将原始的xml文件覆盖掉,这样docx中的文本内容都已经是我们想要的了,再去掉水印图片即可,我们可以在最外层的目录准备一张A站的水印图片,以rb格式打开,凡是在word/media目录下与其相等,统一删除掉,代码如下
import osimgs = os.listdir('.')fp = open('D:\\***\\logo.jpeg', 'rb')e = fp.read()lst = []for im in imgs: with open(im, 'rb') as f: im_ = f.read() if e == im_: lst.append(im)for x in lst: os.remove(x)fp.close()
处理完成之后我们再以docx后缀对其重新命名,最终看到的word文档水印也已经消失了。
至于后面如果想再生成PDF文档,增加PDF水印,就已经是很简单的操作了,当然docx2pdf 是需要进行pip安装的。
from docx2pdf import convertconvert("input.docx", "output.pdf")