python实现docx的批注(comments)插入

项目需要实现自动在docx中插入批注,首选为python,python中有docx库,但是到目前为止还是未支持插入批注功能,但是在python-docx项目中,有人提出了这个问题,作者scanny给出了相关指导。

总结一下大致思路为:解压docx文件后会得到很多文件及文件夹,对比插入批注和未插入批注的解压文件发现:插入批注会新增一个word/comments.xml文件,并且会修改word/_rels/document.xml.rels和word/document.xml,后续插入新的标注只会修改word/comments.xml和word/document.xml。所以只需要搞清楚document.xml.rels、comments.xml、document.xml的变化规律,就可以实现批注插入的自动化。

大家可以尝试将docx文件重命名为.zip,然后解压,手动修改里面的文件信息,再压缩回.zip,再重命名为docx,关于压缩回.zip可能出现的问题,参考这里

以下为未插入批注解压文件结构:

python实现docx的批注(comments)插入_第1张图片

以下为插入批注的文件结构:

python实现docx的批注(comments)插入_第2张图片

最明显的区别在于新增了word/comments.xml文件其次还有word/_rels/document.xml.rels、word/document.xml内容的变化。

首先对比word/_rels/document.xml.rels文件内容的变化

插入批注前:


插入批注后:


其次对比word/document.xml内容变化:

插入前:


这是一段文本,等待插入批注

插入后:


这是一段文本,等待插入批注

对比插入一个批注和插入两个批注的区别:

插入一个:


这是一个批注

插入两个:


这是一个批注这是第二个批注

区别大家可以自己尝试。

不多说,上实现代码:

运行python3 [code.py代码文件名] [docx文件路径] [需要被批注的文本内容] [批注内容]

例: python3 insert_comments.py /Users/guochuanxiang/Desktop/comments.docx 文本 批注

# coding:utf-8

import sys
from zipfile import ZipFile
import os
import shutil
import re


def write_comments(comments_file_content, comments):  # comments: [被批注文本,批注]
    comments_id = comments[2]
    print ('generate comments.xml content....')
    tmp = '{}'.format(comments_id, comments[1])
    content_comments = comments_file_content[:-13]+tmp
    return content_comments


def write_document(document_file_content, comments):
    comments_id = comments[2]
    print ('generate document.xml content....')
    tmp = '{}'.format(comments_id,comments[0],comments_id,comments_id)
    content_document = document_file_content.replace(comments[0],tmp,1)
    return content_document


def write_rel(rel_file_content, comments):
    if rel_file_content.find('comments.xml') == -1:
        print ("not find comments.xml")
        content_rel = rel_file_content[:-16]+''.format('rId9')
        print(content_rel)
        return content_rel
    else:
        print('get comments.xml in rels file')
        return rel_file_content

def run(file_path='/Users/guochuanxiang/Desktop/test.docx',comments=['内容', '批注1']):

    doc_file = open(file_path, 'rb')
    doc = ZipFile(doc_file)
    doc.extractall()    #解压文件
    print ('extracting....')
    file_name = doc.namelist()  #获取所有文件名
    if 'word/comments.xml' not in file_name:
        print ('create comments.xml')
        comments_file = '\n'
        comments.append(0)
    else:
        comments_file = doc.read('word/comments.xml').decode('utf-8')   #获取comments.xml内容
        comment_id = re.compile(r'(?<=id=")\d+')    #寻找所有comments id
        comment_id = int(max(comment_id.findall(comments_file)))+1 #设置批注id为最大+1
        comments.append(comment_id)
    document_file = doc.read('word/document.xml').decode('utf-8')   #获取document.xml内容
    rel_file = doc.read('word/_rels/document.xml.rels').decode('utf-8')     #获取rel内容
    doc.close()
    doc_file.close()

    comments_g = write_comments(comments_file, comments)  #获取添加批注后comments.xml内容
    document = write_document(document_file, comments)  #获取添加批注后doucment.xml内容
    rel = write_rel(rel_file, comments) #获取添加批注后rel内容
    print ('get all content')
    print('writing document.xml.rels...')
    r_f = open('word/_rels/document.xml.rels','w')
    r_f.write(rel)
    r_f.close()
    print('done')
    print ('writing comments.xml...')
    c_f = open('word/comments.xml','w') #将插入批注的comment内容写入comments.xml
    c_f.write(comments_g)
    c_f.close()
    print('done')
    print('writing document.xml....')   #将插入批注的document内容写入document.xml
    d_f = open('word/document.xml','w')
    d_f.write(document)
    d_f.close()
    print('done')
    os.remove(file_path)     #删除原docx

    print('creat commented docx....')
    new_file = ZipFile(doc.filename,mode='w')   #新建空docx
    if 'word/comments.xml' not in file_name:
        print ('add {}'.format('word/comments.xml'))
        new_file.write('word/comments.xml')
    try:
        for name in file_name:
            if os.path.isfile(name):
                print('add {}'.format(name))
                new_file.write(name)    #将文件压缩回docx
    finally:
        print('closing')
        new_file.close()
    for name in file_name:
        if os.path.exists(name):
            if os.path.isfile(name):
                os.remove(name)
            else:
                shutil.rmtree(name)
    print('done')


if __name__ == '__main__':
    file_path = sys.argv[1]
    text = sys.argv[2]
    comment = sys.argv[3]
    comments = [text,comment]
    print (comments)
    run(file_path,comments)

总结:按scanny的说法,python-docx有提供在xml里插入内容的方法,但是我没用过这个模块,所以没有深究如何用docx实现,目前这种实现方法有局限性,如果一段文本被批注多次可能会出现问题,可能需要使用docx模块的插入方法可以解决,大佬们可以尝试一下

想深入了解docx文档结构,可以点击这里

你可能感兴趣的:(python,docx)