今天给大家分享使用python的Zipfile模块巧解word批量生成问题,这里提供两种方案给大家参考。

任务需求

现有一包含目标数据的excel文档,需要将其中的每一行数据的对应内容替换到指定word中,并逐一保存。

任务拆解

首先查看word文档格式,可以看到文件后缀为.doc,需要替换的是正文部分红框中的英文部分。

python生成word目录(手把手教你使用python的zipfile模块巧解word批量生成问题)(1)

目标数据excel文档,将excel中的对应列下的数据替换到word文档中。

python生成word目录(手把手教你使用python的zipfile模块巧解word批量生成问题)(2)

excel数据比较规整无需二次处理,如果列名与word文档中不对应或者没有,则需要调整或新增。如此一来只要考虑如何读取doc文件并按一定逻辑进行替换即可。

任务方案

方案一:使用python-docx.Document读取word文档

在这次需求之前我并没有用过python操作word文档,感谢交流群内各位大佬相助,从Python Excel Word一秒制作百份合同文章中虽然跟着步伐编写代码,而当执行时报错了,目标字符串没有被替换上等等。

将上述问题逐一解决后,输入目标文件路径及输入路径就大功告成了。源码:

from copy import deepcopyfrom pathlib import Pathfrom win32com import client as wc # pip install pypiwin32from docx import Document # pip install python-docximport pandas as pd# python-docx不能处理doc文档,使用win32com转存为docx文档def doctransform2docx(doc_path):docx_path = doc_path 'x'suffix = doc_path.split('.')[1]assert 'doc' in suffix, '传入的不是word文档,请重新输入!'if suffix == 'docx':return Document(doc_path)word = wc.Dispatch('Word.Application')doc = word.Documents.Open(doc_path)doc.SaveAs2(docx_path, 16) # docx为16doc.Closeword.Quitreturn Document(docx_path)# 替换docx中的特定字符,由于run方法在有格式的docx文件中展示效果很差,故将docx中的文本的需要填充出英文字符占位def replace_docx(name, values, wordfile, path_name='Company'):wordfile_copy = deepcopy(wordfile) # 防止原文件被篡改,deepcopy为副本for col_name, value in zip(name, values):if col_name == 'Company':path_name = str(value)for paragraphs in wordfile_copy.paragraphs:for run in paragraphs.runs:run.text = run.text.replace(col_name, str(value))# docx文档替换完毕,另存为,一定要用绝对路径wordfile_copy.save(f'{save_folder}/{path_name}.docx')if __name__ == '__main__':# 定义需处理的文件路径doc_path = r"D:\solve_path\单位.doc"excel_path = r"D:\solve_path\信息.xls"save_folder = Path('D:/docx_save')save_folder.mkdir(parents=True, exist_ok=True) # 文件夹没有时自动创建# 获取excel数据data = pd.read_excel(excel_path)wordfile = doctransform2docx(doc_path)data_save = data.apply(lambda x: replace_docx(x.index, x.values, wordfile), axis=1)

在我以为大功告成之际,问题来了,原文档中的方框没了(漏!!!) 效果图:

python生成word目录(手把手教你使用python的zipfile模块巧解word批量生成问题)(3)

解决了格式却解决不了特殊字符问题,秃了啊……,我想python-docx中一定有相应的解决方案,但是我初次尝试,对其中源码部分犹如天书般的存在,在多次调用方法下发现其中的一个参数输出,wordfile.part.blob:

python生成word目录(手把手教你使用python的zipfile模块巧解word批量生成问题)(4)

输出内容让我想起了之前解密excel时看到的文件开头,xml文件,然后首先尝试替换其中文本,原以为会像run.text = run.text.replace(col_name, str(value))一样即可,然而报错了,禁止修改。

python生成word目录(手把手教你使用python的zipfile模块巧解word批量生成问题)(5)

方案二:zipfile巧解word文档

正当我认为别无他法时,就此作罢时,百度百科帮助了我:

python生成word目录(手把手教你使用python的zipfile模块巧解word批量生成问题)(6)

docx文档本质上就是xml文件,emmmm,很妙,之前为了提取xlsx中的图片解压缩过xlsx文件然后提取,果然可行,替换的主体文件就是word文件夹下的document.xml文件

python生成word目录(手把手教你使用python的zipfile模块巧解word批量生成问题)(7)

当然在代码编写前首先尝试能不能手动复原为docx,用7z默认参数还原失败,经过多番寻找,用zip类型压缩即可,软件不限,手动解压及替换字符压缩均成功,开始敲代码。除习惯性用pandas读取excel文件外,也不用安装其他包,在现用python3.7中均为内置包。使用zipfile对压缩类文件进行解压,文章学习来源:

python中如何压缩和解压缩文件https://www.cnblogs.com/rongge95500/p/11271764.html

文章中写得很详细,我仅把os.path改写成pathlib。但在对目录下文件进行压缩还原至docx文档时出现了问题:

源码:

from shutil import rmtreeimport zipfilefrom copy import deepcopyfrom pathlib import Pathfrom win32com import client as wc # pip install pypiwin32import pandas as pd# doc文档不包含所需xml文件,使用win32com转存为docx文档def doctransform2docx(doc_path):docx_path = doc_path 'x'suffix = doc_path.split('.')[1]assert 'doc' in suffix, '传入的不是word文档,请重新输入!'if suffix == 'docx':return Path(doc_path)word = wc.Dispatch('Word.Application')doc = word.Documents.Open(doc_path)doc.SaveAs2(docx_path, 16) # docx为16doc.Closeword.Quitreturn Path(docx_path)# docx文档解压def docx_unzip(docx_path):docx_path = Path(docx_path) if isinstance(docx_path, str) else docx_pathupzip_path = docx_path.with_name(docx_path.stem)with zipfile.ZipFile(docx_path, 'r') as f:for file in f.namelist:f.extract(file, path=upzip_path)xml_path = upzip_path.joinpath('word/document.xml')with xml_path.open(encoding='utf-8') as f:xml_file = f.readreturn upzip_path, xml_path, xml_file# 讲文件夹中的所有文件压缩成docx文档def docx_zipped(docx_path, zipped_path):docx_path = Path(docx_path) if isinstance(docx_path, str) else docx_pathwith zipfile.ZipFile(zipped_path, 'w', zipfile.zlib.DEFLATED) as f:for file in docx_path.glob('**/*.*'):f.write(file, file.as_posix.replace(docx_path.as_posix '/', ''))# 删除生成的解压文件夹def remove_folder(path):path = Path(path) if isinstance(path, str) else pathif path.exists:rmtree(path)else:raise "系统找不到指定的文件"# 替换docx中的特定字符,重新保存document.xml至需要压缩的目录下def replace_docx(name, values, xml_file, xml_path, unzip_path, path_name='Company'):xml_path = Path(xml_path) if isinstance(xml_path, str) else xml_pathxml_file_copy = deepcopy(xml_file) # 深复制xml内容for col_name, value in zip(name, values):if col_name == 'Company':path_name = str(value)xml_file_copy = xml_file_copy.replace(col_name, str(value))with xml_path.open(mode='w', encoding='utf-8') as f:f.write(xml_file_copy)# xml文档替换完毕,通过zipfile重新压缩另存为docx文档docx_zipped(unzip_path, f'{save_folder}/{path_name}.docx')if __name__ == '__main__':# 定义需处理的文件路径doc_path = r"D:\solve_path\单位.doc"excel_path = r"D:\solve_path\信息.xls"save_folder = Path('D:/docx_save')save_folder.mkdir(parents=True, exist_ok=True) # 文件夹没有时自动创建# 获取excel数据data = pd.read_excel(excel_path)docx_path = doctransform2docx(doc_path)unzip_path, xml_path, xml_file = docx_unzip(docx_path)data_save = data.apply(lambda x: replace_docx(x.index, x.values, xml_file, xml_path, unzip_path), axis=1)remove_folder(unzip_path)

打开生成的文件,方框没有消失,下划线也在。

python生成word目录(手把手教你使用python的zipfile模块巧解word批量生成问题)(8)

总结

经过几番尝试后,也是我的学识不深,在跌跌撞撞中找到一种既能替换docx中的字符串也不会改变原有格式的方案,相信一定会有更好的方案,只是此时我没有找到,时间是不停地向前的,我也不应落下,以求共同富贵。如对此有疑问欢迎评论区留言。

PS: 如果电脑安装的是WPS,可以尝试手动转换doc格式为docx格式,再进行批量操作。

于二零二一年十一月二十八日作

版权声明:本文为CSDN博主「宿者朽命」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_46281427/article/details/121588786

,