python-docx通过标题给整个docx文档排序
上班有任务之后感觉效率就提高了,果然,人活着还是得有目标、有动力。
我们发现word文档的内容顺序不对,但是有几百条标题,上千页文档,手动排序虽然也可以,但是费时费力,而且万一以后需求又有了变化就白给了,所以依然以python-docx为基础,开发了一个sort脚本
预备知识
我们首先要知道docx的格式,docx的主体本质上是一个xml
<w:body>
body之间的内容就是word文档的内容
w:body><w:p>
p之间的内容就是文档内一个段落的内容
w:p><w:r>
r之间的内容是段落内的小块,当一个段落内字体以及格式不一样是,会存在多个run
w:r>这个模块这表示该段落p(模块r)的类型
<w:pPr>p:<w:Style w:val = "Heading 1">标题r:<w:b/>加粗<w:i/>斜体
w:pPr><w:t>
这个部分用于存放文本内容
w:t>
上述格式的级别包含关系示例:
<w:body><w:p><w:pPr><w:Style w:val = "Heading 1">w:pPr><w:r><w:pPr><w:b/>w:pPr><w:t>Heading1w:t>w:r>w:p>
w:body>
上述示例,在word中显示就是一个加粗的,段落与字体格式为Heading 1的,文本显示为Heading1的一级标题
本方法的适用类型
Notice
仅适用于以下结构的文档,文档的主体部分仅存在段落结构,所有其他的内容都归于段落内
<w:body>#段落1<w:p>标题w:p>#段落2<w:p>正文、图片w:p>#段落3<w:p>表格w:p>...#段落n<w:p>标题nw:p>...
<w:body>
排序思路
对于这种结构的文档,那么我们的思路就很清晰了:
- 遍历文档的所有段落
- 找到文档格式(Style)为标题(Heading)的段落
- 把所有标题段落的文本(Text)取出生成一个StringList,并按照自己的需求排序
- 最后根据StringList的顺序,为文档重新排序,即移动paragraph
这样我们就完成了对于文档的排序
实际操作
import
from os import write
from sys import flags
from docx import Document
from docx.shared import Inches
str_list_sort
第一个函数是排序函数,我们将一个二维列表根据列表的第一个元素(我们的标题)的字母(不区分大小写)从小到大排序,最后返回标题的String,以及标题对应的段落的开始序号和结束序号
[HeadingStr, ParagraphStartIndex, ParagraphEndIndex]
本函数的原型是在网上抄的,感谢一波忘记是谁的不知名网友
#将所有字符串转化为小写进行比较排序
def str_list_sort(string_list):listtemp = []auxiliary_list = []for i in range(0,len(string_list)):listtemp.append(string_list[i][0].lower())listtemp.append(string_list[i][0])listtemp.append(string_list[i][1])listtemp.append(string_list[i][2])auxiliary_list.append(listtemp)listtemp = []#将字符串列表转化为:[['str1','str1.lower()'], ['str2','str2.lower()'], ...]的格式#auxiliary_list = [(x.lower(), x, index, endindex) for x in string_list[0] for index in string_list[1] for endindex in string_list[2]]#排序auxiliary_list.sort()#读取排序后的字符串列表(读取第二位字符串)new_list = [(x[1], x[2], x[3]) for x in auxiliary_list]return new_list
insertParagraph
第二个函数是将原始文档中的段落,插入新的文档中
输入:段落起始序号,段落结束序号,原始文档的路径,新文档的对象,新文档的插入起始位置
def insertParagraph(begin, end, filePath, dNew, startIndex):'''flag = Falsewrite = FalsedOri = Document(filePath + 'MC-Basic Document User.docx')for element in dOri.element.body:d = Document()d.element.body.append(element)if len(d.paragraphs) > 0:p = d.paragraphs[0]#print(p.text)if len(p.text) > 0:if str == p.text:flag = Truewrite = Trueelif "Heading 1" == p.style.name:flag = Falseif write:breakif(flag):dNew.element.body.append(element)'''#print(1)dOri = Document(filePath)i = beginwhile i <= end:dNew.element.body.append(dOri.paragraphs[i]._p)end = end - 1
这个函数非常简短着重解释以下几点:
'''
这个变量代表:docx类.第i个段落方法.指向该段落本体的指针
因此,当将该变量append到新文档中,会导致旧文档的paragraphs的第i个成员丢失
于是,我们不需要改变序号i,一直读取paragraphs[i]._p,即可实现顺序添加段落
于是,我们通过end-- 的方式完成遍历添加段落
'''
dOri.paragraphs[i]._p
'''
这个变量代表:docx方法类.指向docx本体类的指针.下属的body元素
通过append的方法,把paragraph的本体添加进去
通过这种方法,能够实现对paragraph的整体添加,而不是只加入文本和格式,
确保了表格,超链接,标签等等内容不会丢失
'''
dNew.element.body
sortHeading
第三个函数就是实现通过标题对文档进行sort的函数
输入原始文档的对象(其实好像不需要)和路径
def sortHeading(document, fileName):'''为了确保文档设置的格式统一性,我们不使用Document()创建新的空白文档而是通过读取原始文档,再将文档的内容清空来实现创建空白文档'''#generate blank document with original Styles and SettingsdNew = Document(fileName)dNew._body.clear_content()#define tool variableheadStr = []tempList = []index = 0end = 0#loop all paragraphsfor p in document.paragraphs:'''我们希望能够不遗漏任何东西,完成全部迁移由于在docx中,标题级别的段落和正文级别的段落在xml格式中都属于paragraph,不存在包含关系,只存在Style的区别,于是我们寻找style为Heading 1的段落,并将此时的序号index作为该标题的起始序号,并将上一个标题的终止序号设置为index - 1将这三个元素保存到tempList中,再将tempList保存袋headStr中,通过str_list_sort实现标题排序'''if p.style.name == 'Heading 1':endindex = index - 1end = max(end, endindex + 1)if len(tempList) > 0:tempList.append(endindex)headStr.append(tempList)tempList = []tempList.append(p.text)tempList.append(index)index = index + 1headStr = str_list_sort(headStr)'''根据排完序的二维列表生成新的docx文档'''for i in range(0, len(headStr)):insertParagraph(headStr[i][1], headStr[i][2], fileName, dNew, headStr[0][1])#insertParagraph(end, len(document.paragraphs), document, dNew)return dNew
至此我们就完成了对一个文档基于标题的重新排序
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
