python-docx通过标题给整个docx文档排序

博客介绍了使用Python对Word文档标题进行排序的方法。因手动排序几百条标题、上千页文档费时费力,故以python - docx为基础开发sort脚本。先介绍docx格式本质是xml,说明了适用文档结构,阐述排序思路,最后给出实际操作的几个函数,完成文档基于标题的重新排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上班有任务之后感觉效率就提高了,果然,人活着还是得有目标、有动力。
我们发现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>Heading1</w: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>
		标题n
	</w:p>
	...
<w:body>

排序思路

对于这种结构的文档,那么我们的思路就很清晰了:

  1. 遍历文档的所有段落
  2. 找到文档格式(Style)为标题(Heading)的段落
  3. 把所有标题段落的文本(Text)取出生成一个StringList,并按照自己的需求排序
  4. 最后根据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 = False
    write = False
    dOri = 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 = True
                    write = True
                elif "Heading 1" == p.style.name:
                    flag = False
                    if write:
                        break
        if(flag):
            dNew.element.body.append(element)
    '''
        #print(1)
    dOri = Document(filePath)
    i = begin
    while 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 Settings
    dNew = Document(fileName)
    dNew._body.clear_content()

	#define tool variable
    headStr = []
    tempList = []
    index = 0
    end = 0

	#loop all paragraphs
    for 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 - 1
            end = max(end, endindex + 1)
            if len(tempList) > 0:
                tempList.append(endindex)
                headStr.append(tempList)
                tempList = []
            tempList.append(p.text)
            tempList.append(index)
        index = index + 1
    headStr = 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

至此我们就完成了对一个文档基于标题的重新排序

好的,下面是对这个功能的一个详细介绍以及如何实现的解答: 为了完成 `main()` 函数的需求,我们可以借助 Python 的第三方库 **`python-docx`** 来操作 Word 文件,并通过分析每一段文本的颜色属性来统计非黑色和非默认颜色的文字数量。以下是具体的步骤说明及解决方案。 ### 解决方案 1. **导入必要的模块** - 使用 `docx.Document` 打开 `.docx` 文件。 - 利用 `RGBColor` 对象提取字体的颜色信息。 2. **遍历文档内容** - 遍历每个段落的所有运行 (runs),检查其颜色是否设置 (`font.color.rgb`)。 - 如果设置了颜色,则将其转换为十六进制格式;如果没有显式设定颜色,默认跳过该部分。 3. **统计数据** - 创建一个字典用于存储各种颜色及其对应的出现次数。 - 确保只记录非黑色(即不是 `(0, 0, 0)` 或等价的 `'000000'`)以及其他默认无色的情况。 4. **排序并返回结果** - 根据颜色计数对字典进行降序排列。 - 提取出前三名颜色值作为最终的结果元组。 下面是示例代码框架: ```python from collections import defaultdict from operator import itemgetter from docx import Document from docx.shared import RGBColor def count_colors(doc_path): color_count = defaultdict(int) document = Document(doc_path) for paragraph in document.paragraphs: for run in paragraph.runs: if run.font and run.font.color and run.font.color.rgb: rgb_color = run.font.color.rgb hex_color = f"{rgb_color[0]:02X}{rgb_color[1]:02X}{rgb_color[2]:02X}" # 排除黑色 '000000' if hex_color != "000000": color_count[hex_color] += len(run.text) # 按字符长度计算 sorted_colors = sorted(color_count.items(), key=itemgetter(1), reverse=True)[:3] return tuple([color for color, _ in sorted_colors]) # 主函数入口 def main(): file_name = "data78.docx" result = count_colors(file_name) print(result) # 输出类似 ('FF0000', '00FF00', '0000FF') if __name__ == "__main__": main() ``` #### 关键点解析: - 我们首先初始化了一个 `defaultdict` 类型的对象 `color_count`, 它能够自动处理未存在的键的问题。 - 文档中的每一个段落都由若干 runs 组成,因此我们需要逐一访问这些 runs 并获取它们所包含的内容及相关样式信息。 - 字体颜色通常以三元组的形式表示红绿蓝分量值,在此我们把他们转成了 HTML/CSS 常见的标准十六进制编码形式以便统一比较和展示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值