在用python实现一个小工具的时候,需要修改word中标签的内容,因为是在linux上操作,无法使用win32com的方式,只能用python-docx。在网上找了一圈没有找到完整的解决方案,在此整理一下解决的思路。
一、实现word中标签的定位
首先,要找到word中标签的位置。在StackOverFlow上找到了一篇文章(链接:https://stackoverflow.com/questions/24965042/python-docx-insertion-point),可以通过分析docx的lxml,获取标签所在的位置。但是作者只是找到了起始位置,然后就把自己的内容直接复制过去了,这不是我想实现的。要把标签的内容全部修改,需要把起始位置和结束位置都找到。模仿这篇文章的做法,自己写了两个函数:
import docx
from docx.oxml.shared import qn
#查找标签的起始位置(所在的段落)
def get_bookmark_par_element_start(document, bookmark_name):
doc_element = document.part.element
bookmarks_list = doc_element.findall('.//' + qn('w:bookmarkStart'))
for bookmark in bookmarks_list:
name = bookmark.get(qn('w:name'))
if name == bookmark_name:
par = bookmark.getparent()
if not isinstance(par, docx.oxml.CT_P):
return 2
else:
return par, bookmark.get(qn('w:id'))
return 1
#查找标签的结束位置(所在的段落)
def get_bookmark_par_element_end(document, bookmark_id):
doc_element = document.part.element
bookmarks_list = doc_element.findall('.//' + qn('w:bookmarkEnd'))
for bookmark in bookmarks_list:
name = bookmark.get(qn('w:id'))
if name == bookmark_id:
par = bookmark.getparent()
if not isinstance(par, docx.oxml.CT_P):
return 2
else:
return par
return 1
注意因为docx的w:bookmarkEnd标签并没有 w:name属性,只能通过w:id来确定,所以在获取w:bookmarkStart标签的时候,要把w:id一并获取,以方便查找对应的结束标签。
二、修改标签内容
确定了标签起始位置、结束位置之后,事情就好办了:
第一步:修改标签内段落的内容;
第二步:如果修改后段落数量少于原来标签内的段落数量,需要删除掉原来标签内多出来的部分。
代码如下:
def set_bookmark_values(document, bookmark_name, texts):
tmp_doc_body = document.part.element.body
# print(docx.oxml.xmlchemy.serialize_for_reading(tmp_doc_body))
bookmark_par, id = get_bookmark_par_element_start(document, bookmark_name)
bookmark_par_parent = bookmark_par.getparent()
index = bookmark_par_parent.index(bookmark_par) + 1
bookmark_end_par = get_bookmark_par_element_end(document, id)
bookmark_end_par_parent = bookmark_end_par.getparent()
index_end = bookmark_end_par_parent.index(bookmark_end_par) + 1
if isinstance(texts, list):
document.paragraphs[index - 1].text = texts[0]
for i in range(1, len(texts)):
para_number = index + i - 1
if para_number < index_end - 1:
document.paragraphs[para_number].text = texts[i]
else:
document.paragraphs[para_number].insert_paragraph_before(texts[i])
for i in range(index_end - 1, para_number, -1):
para = document.paragraphs[i]
p = para._element
p.getparent().remove(p)
para._p = para._element = None
else:
document.paragraphs[index - 1].text = texts
for i in range(index_end - 1, index - 1, -1):
para = document.paragraphs[i]
p = para._element
p.getparent().remove(p)
para._p = para._element = None
删除段落内容的代码来源于:https://blog.csdn.net/auspark/article/details/106658341,具体内容希望大家直接看源网页。
三、测试运行的结果
打开一个包含标签”bookmark1“的docx文档,然后把标签内的内容修改为两个段落”content1“、”content2“:
doc = docx.Document('模板.docx')
set_bookmark_values(doc, 'bookmark1', ['content1', 'content1'])
doc.save('新模板.docx')
四、其他没有解决的问题
1。众所周知,docx的标签可以在一个段落内任意位置开始,不一定包含完整的段落,但是以上代码只能定位到段落,所以不能精确修改内容。现在我的要求已经实现了,精力不足,不往下研究了。大家有解决方案欢迎分享。
2。按照以上代码处理之后,原来的bookmark定义就没有了……