自Office 2007以来,新推出的.docx文件可以无损转换成OpenXML格式,以便于第三方工具生成、修改Word文件。本文以Python为背景,简单解析OpenXML中的常用元素,主要用作个人备忘。
个人邮箱为[email protected],欢迎来信交流。
本文目前仍在施工中,因疫情影响,完工日期未定QAQ
与
、
Python解析OpenXML的两个常用库:python-docx与lxml,可通过pip安装,若对两个库不熟悉请参阅以下资料:
lxml.etree.Element
中的.addprevious()
与.addnext()
本文举例所用的文本内容,为唐朝诗人张若虚的《春江花月夜》,全文如下:
《春江花月夜》
【唐·张若虚】
春江潮水连海平,海上明月共潮生。
滟滟随波千万里,何处春江无月明?
江流宛转绕芳甸,月照花林皆似霰。
空里流霜不觉飞,汀上白沙看不见。
江天一色无纤尘,皎皎空中孤月轮。
江畔何人初见月?江月何年初照人?
人生代代无穷已,江月年年只相似。
不知江月待何人,但见长江送流水。
白云一片去悠悠,青枫浦上不胜愁。
谁家今夜扁舟子?何处相思明月楼?
可怜楼上月徘徊,应照离人妆镜台。
玉户帘中卷不去,捣衣砧上拂还来。
此时相望不相闻,愿逐月华流照君。
鸿雁长飞光不度,鱼龙潜跃水成文。
昨夜闲潭梦落花,可怜春半不还家。
江水流春去欲尽,江潭落月复西斜。
斜月沉沉藏海雾,碣石潇湘无限路。
不知乘月几人归?落月摇情满江树。
将全文以纯文本方式粘贴到新建的word文档中,不修改任何格式,直接将文件保存为”春江花月夜.docx“。
(非必需) 之后,再将文件另存为”word xml(*.xml)“文件,命名为”春江花月夜.xml“,并使用喜欢的xml查看器打开(个人推荐安装了XML Tools插件的Notepad++)。
本文将以此为例。
准备工作完成后,先看一个OpenXML的简单例子(注意这不是一个完整的word文件):
<w:body xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex">
<w:p w:rsidR="00691F43" w:rsidRDefault="00691F43" w:rsidP="00691F43">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
w:rPr>
<w:t>《春江花月夜》w:t>
w:r>
w:p>
<w:p w:rsidR="00691F43" w:rsidRDefault="00691F43" w:rsidP="00691F43">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
w:rPr>
<w:t>【唐·张若虚】w:t>
w:r>
w:p>
w:body>
这是body中的开头两个自然段。OpenXML采用了命名空间(namespaces),参考XML命名空间。在本例中,根标签是xmlns:XX
就是用来说明命名空间的,本例中的命名空间有3个:w
、wpc
与cx
,实际上一个word文件可能有十几个命名空间,但常用的就是w
。
在word中依次选择:文件——另存为——保存类型选择“Word XML 文档(*.xml)“——保存。
一、仅读取,不修改:
from docx import Document
from lxml import etree
doc = Document('春江花月夜.docx')
body_xml_str = doc._body._element.xml # 获取body中的xml
body_xml = etree.fromstring(body_xml_str) # 转换成lxml结点
print(etree.tounicode(body_xml)) # 打印查看
for p in doc.paragraphs:
p_xml_str = p._p.xml # 按段落获取xml
p_xml = etree.fromstring(p_xml_str) # 转换成lxml结点
print(etree.tounicode(p_xml)) # 打印查看
注意:修改此方式获取的xml,不会影响原word文件。
二、如果想直接修改原word文件的xml,可以将docx.Document中,变量类型type
是以CT_
开头的(如doc._body._element
、p._p
、doc.styles._element
等)直接当作lxml.etree.Element
使用(虽然它们之间似乎没有继承关系) 。
print(type(doc._body._element)) #
print(type(p._p)) #
print(type(doc.styles._element)) #
例如:
from docx import Document
from lxml import etree
doc = Document('春江花月夜.docx')
body_xml = doc._body._element # 获取body中的xml
print(etree.tounicode(body_xml)) # 打印查看
for p in doc.paragraphs:
p_xml = p._p # 按段落获取xml
print(etree.tounicode(p_xml)) # 打印查看
本节的主要参考资料为OpenXML微软官方文档。
(打不开的朋友可复制地址到浏览器:https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing,将地址中的en-us改成zh-cn可浏览机翻的简体中文版本)
还没想好这里的run应该怎么翻译,您有好的建议的话欢迎留言或来信交流。
与
pRr
意为Paragraph Properties,通常用作表示段落的相关属性。
rPr
意为Run Properties,通常用作表示字体的相关属性。
rFonts
意为RunFonts,用于表示不同语系下的字体,以及当前语种,点此查看官方文档。
用”语系“这个词可能不太恰当,但一时想不到别的词来描述了。您有好的建议的话欢迎留言或来信交流。
如果不显式指明字体,此段文字会使用上一级指明的字体。
目前rFonts
支持四个语系,分别为:
{
'ascii':'ASCII Font. Represents the following attribute in the schema: w:ascii',
'eastAsia':'East Asian Font. Represents the following attribute in the schema: w:eastAsia',
'hAnsi':'High ANSI Font. Represents the following attribute in the schema: w:hAnsi',
'cs':'Complex Script Font. Represents the following attribute in the schema: w:cs',
}
rFonts
使用w:hint
说明此段文本具体使用哪种语系的字体。
例如,将一段汉字的中文字体与西文字体全部设置为宋体:
<w:r>
<w:rPr>
<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体" w:cs="宋体" w:hint="eastAsia"/>
w:rPr>
<w:t>这一段文字字体为宋体。w:t>
w:r>
、
sz
表示Non-Complex Script Font Size,简单理解的话,就是单字节字符(如ASCII编码字符等)的大小。
szCs
表示Complex Script Font Size,可以简单理解为双字节字符(如中日韩文字、阿拉伯文等)的大小。
如果不显式指明字号,此段文字会使用上一级指明的字号。
sz的官方资料中这句话The font sizes specified by this element’s val attribute are expressed as half-point values 说明,
与
中的数值是实际磅值的两倍(原文翻译为”此元素的数值代表的字体大小,表示半点值)。
例如,一个五号汉字,考虑如下中文字号与磅值的对应关系,其磅值为10.5,翻倍为21,所以应该有
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:sz w:val="21"/>
<w:szCs w:val="21"/>
w:rPr>
<w:t>这一段文字是五号字w:t>
w:r>
SZ_ZH_PT = { # 中文字号与磅值对应关系
"八号": 5,
"七号": 5.5,
"小六": 6.5,
"六号": 7.5,
"小五": 9,
"五号": 10.5,
"小四": 12,
"四号": 14,
"小三": 15,
"三号": 16,
"小二": 18,
"二号": 22,
"小一": 24,
"一号": 26,
"小初": 36,
"初号": 42,
}
正常来说,每个run中都应该有
结点,此结点包含的内容就是真实显示在word中的文字。
python-docx中的paragraph.text
和run.text
函数,返回的就是
结点中的内容,例如:
from docx import Document
doc = Document('春江花月夜.docx')
for paragraph in doc.paragraphs:
print('本段内容长度为%d' % len(paragraph.text))
for i, run in enumerate(paragraph.runs):
print('第{}个run的内容:{}'.format(i, run.text))
Word会记录每一次对于文件的修改,并对修订版本进行随机编号。在OpenXML中,修订版本号通过rsid表示,具体的有w:rsidR
、w:rsidRPr
等。
具体的版本更迭信息,保存在
下的
中。
标注过拼音的字符,在word中表现为一块域(field)。在OpenXML中,可看作是在
中,用
替换了
结点。
一个典型的注音xml:
<w:ruby>
<w:rubyPr>
<w:rubyAlign w:val="center"/>
<w:lid w:val="zh-CN"/>
w:rubyPr>
<w:rt>
<w:r>
<w:rPr>w:rPr>
<w:t>zhōngw:t>
w:r>
w:rt>
<w:rubyBase>
<w:r>
<w:rPr>w:rPr>
<w:t>中w:t>
w:r>
w:rubyBase>
w:ruby>