当前位置:   article > 正文

续:python 基于-docx库解决docx自动编号识别问题的一种参考方案(附详解、附代码)_python word 编号

python word 编号

2024.5.31更新2点:1、发现有些中文自动编号不是叫chineseCounting,后面还带了thousand什么的,所以 if num_fmt == "chineseCounting" 要改为 if "chineseCounting" in num_fmt。2、之前对自动编号假设只有3级,虽然看起来数字是连续的,但实际上每个都可能是一组新的自动编号的第一个,对此,修正后对应代码应为:

start_lv123 = [i*0 for i in range(len(List_of_dict))]  #原本是start_lv123=[0,0,0]

for k in range(len(List_of_dict)):
if "start" in List_of_dict[k].keys():
start_lv123[k]=int(List_of_dict[k]["start"].split('\'')[0])


我的上一次文章大概说明了基于docx库解决自动编号识别问题的原理和结论(参见上一篇文章http://t.csdnimg.cn/gozU9),在这篇文章进一步说明一些细节问题,最后附上代码。

我这里有一个text.docx文档(见下图),带有2级自动编号:一、二、 以及1.2. 。以此为例说明。

一、记载“文档自动编号样式信息”的位置在哪里?

自动编号的种类、样式等信息储存在numbering.xml当中(每个docx都是一个zip,手动改变后缀名后可打开压缩包,找到/word/numbering.xml这个文件)。最上层的节点叫numbering,下面与4个节点,分别是2个num和2个abstractNum。每个num节点记录了某个numId值与某个abstractNumId 值的对应关系。节点abstractNum记录了每一个abstractNumId 值对应的自动编号长成什么样、起始序号是几、序号是中文还是数字等等信息。

(一)numId与abstractNumId的这对键值。

而图中xml文件里面最上面那行的“numbering”节点所对应内容信息是在doc.part.numbering_part.numbering_definitions._numbering的位置。它的下一层有4个元素,对应xml的2个num和2个abstractNum节点;同时,它下面有个属性叫num_lst,记载了numId与abstractNumId 的对应关系(实际上与2个num节点应该是记录了同样的内容)。当时我是通过反复逐个print+dir观察出这些规律。

运行结果:

经过dir测试,num_lst是个列表,len(num_lst)结果为2,说明有2个元素。继续对num_lst[0]、num_lst[1]进行dir测试,发现属性当中有numId和abstractNumId这俩。

这就验证了,doc.part.numbering_part.numbering_definitions._numbering.num_lst储存了numId 与abstractNumId的这对值的对应关系。

(二)abstractNum包含样式、起始数字等关键信息。

从xml的abstractNum节点看出,在其下属的lvl节点中,记录了每种有自动编号的numFmt 、lvlText、start等信息,numFmt+lvlText表示其样式,例如“chinesecouting”、“%1、”,意思是中文加顿号,即“一、”“二、”。再如,“decimal”“%1.” 对应的是数字加点,即“1.”“2.”。start表示这种自动编号第一个的序号是几。

首先,找出lvl节点在abstractNum列表的index值。然后查看lvl节点所有子节点的tag,转为字符后,提取最后一组单引号内的字符串,其中就包含了start、numFmt、lvlText等关键信息。

我把每一级自动编号的numId、abstractNumId、start、numFmt、lvlText等信息装在一本本字典中,最后再用列表“List_of_dict”把这些字典装起来。

二、记载了“在文档的什么位置发生了自动编号”的信息在哪里?

文档发生自动编号的信息储存在document.xml当中。每当发生自动编号,就会出现对应numId的值,以及对应在哪一段文字前插入了自动编号。

在document对象中,记录了在哪里发生自动编号的信息位置是在每一个paragraph的paragraph_format.element.pPr.numPr里面,如果发生了自动编号,就会出现"numId"这个属性。用循环+判断,如果遇到了numId,便用这个numId的值去查字典,遍历List_of_dict里面的每一本字典,便能找到对应的样式。

————————————————————————————————

以上是对上一篇文章的补充说明。最后附上完整代码。

————————————————————————————————

  1. from docx import Document
  2. def contains_words(strings, words): #判断是否包含所有关键词
  3. return any(words in s for s in strings)
  4. numbering_list = doc.part.numbering_part.numbering_definitions._numbering
  5. print("numbering_list =",numbering_list)
  6. print("dir(numbering_list) =",dir(numbering_list))
  7. print("dir(numbering_list.num_lst[0])=",dir(numbering_list.num_lst[0]))
  8. List_of_dict =[]
  9. for j in range(len(numbering_list.num_lst)):
  10. List_of_dict.append({}) #非常重要的细节处理:把自动编码转为纯文本。用字典存放一级、二级、三级标题信息。
  11. print(List_of_dict)
  12. for i in range(len(numbering_list.num_lst)): #把num_lst信息复刻到3本字典中
  13. numx = str(numbering_list.num_lst[i].numId)
  14. absNumx = str(numbering_list.num_lst[i].abstractNumId.val)
  15. List_of_dict[i]["numId"]=numx
  16. List_of_dict[i]["absNumId"] =absNumx
  17. for i in range(len(numbering_list.num_lst)):
  18. for nlx in numbering_list:
  19. ii = 999
  20. if hasattr(nlx,"attrib") :
  21. if nlx.attrib.keys()[0].endswith("abstractNumId") :
  22. if nlx.attrib.values()[0] == List_of_dict[i]["absNumId"]:
  23. for nlxx in nlx: #找出lvl标签在abstractNum列表的index值ii
  24. attr_dict = getattr(nlxx,"attrib",None)
  25. if attr_dict is not None:
  26. if contains_words(attr_dict.keys(),"ilvl"):
  27. if nlxx.tag[-3:] == "lvl" :
  28. ii=(nlx.index(nlxx))
  29. break
  30. if ii != 999 :
  31. for kk in nlx[ii]:
  32. kk_tag = kk.tag.split('}')
  33. kk_val = str(kk.attrib.values())
  34. print("kk_tag=",kk_tag,"kk_val=",kk_val)
  35. if len(kk_val)>1:
  36. List_of_dict[i][kk_tag[1]]=kk_val[2:-2]
  37. # print(f"List_of_dict[{i}][{kk_tag[1]}]=",List_of_dict[i][kk_tag[1]])
  38. ii = 999
  39. start_lv123 = [0,0,0]
  40. if len(List_of_dict)>3:
  41. for k in range(3):
  42. if "start" in List_of_dict[k].keys():
  43. start_lv123[k]=int(List_of_dict[k]["start"].split('\'')[0])
  44. else:
  45. for k in range(len(List_of_dict)):
  46. if "start" in List_of_dict[k].keys():
  47. start_lv123[k]=int(List_of_dict[k]["start"].split('\'')[0])
  48. print("start_lv123=",start_lv123)
  49. for d in doc.paragraphs: # 遍历所有段落,每当出现自动编号,在编号与后面紧接的文字之间,加入一份跟自动编号长得一样的纯文本。
  50. val_d = getattr(d.paragraph_format.element.pPr.numPr,"numId",None) #对于每个段落,逐个尝试是否存在numId这个属性,没有就赋值None
  51. if val_d is not None: #如果存在numId这个属性,下一步就拿val_d去查字典,读取应该替换哪种格式是%1、还是(%1) %1.
  52. for dd in range(len(List_of_dict)):
  53. if len(List_of_dict[dd]) > 0:
  54. if List_of_dict[dd]["numId"] != str(val_d.val): #如果对应一级标题的numId没对上号,就找二级标题、三级标题
  55. continue
  56. else: #查字典对上号了,读取对应的编号格式
  57. num_fmt =List_of_dict[dd]["numFmt"]
  58. auto_number = List_of_dict[dd]["lvlText"]
  59. if num_fmt == "chineseCounting":
  60. auto_number = auto_number.replace("%1",shuzizhuanhanzi[start_lv123[dd]])
  61. elif num_fmt == "decimal":
  62. auto_number = auto_number.replace("%1",str(start_lv123[dd]))
  63. print("auto_number= ",auto_number)
  64. d.text = auto_number + d.text
  65. start_lv123[dd] += 1 #对这个自动编码的序号处理完了,记得+1表示下一个

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/795945
推荐阅读
相关标签
  

闽ICP备14008679号