使用python-docx修改word中标签(bookmark)的内容

        在用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定义就没有了……

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值