跳转至

16.3.docx

Windows 10
Python 3.7.7 @ MSC v.1916 64 bit (AMD64)
Latest build date 2020.09.21
python-docx version:  0.8.10

python-docx是Python操作word文档时最常用的库。python-docx可以创建一个word文档,往文档中添加文字、表格、图片,并且可以设置样式;也可以从一个word文档中读取文字、表格、内联的图片。

python-docx具有以下特点:

  1. python-docx虽然可以设置样式,但它的样式设置不能包含word的所有样式。
  2. Word有样式管理器,python-docx可以操作这个管理器,添加自定义的样式到管理器中,或者从管理器选取一个样式加以应用。
  3. python-docx只能读取内联图片。

如果对docx文档的样式设置有较高要求,可以先设置好一个word模板文档,然后用python-docx读入该模板文档,操纵该文档的样式管理器。如果不能用Word自定义样式,又对样式设置有很高要求,只能考虑VBA、win32api等功能齐全的工具了。

读取docx

from docx import Document

读取文本

# 打开 SQL笔记.docx 文档
doc_path = r"datasets/file/SQL笔记.docx"
doc = Document(doc_path)

# 读取每段的文本
pl = [paragraph.text for paragraph in doc.paragraphs]
print(f"一共有 {len(pl)} 个paragraph")
print(pl[100])
一共有 835 个paragraph
1986 年,ANSI 首次制定了 SQL 的标准。修订后的标准以修订年份来命名,例如 SQL:1999、SQL:2003、SQL:2008
等。

读取表格

# 读取所有表格
tables = doc.tables

# 打印第一个表格的内容
for row in tables[0].rows:
    for cell in row.cells:
        print(cell.text, end=' \t | \t')
    print()
含义       |      运算符      |
加法运算     |      +        |
减法运算     |      -        |
乘法运算     |      *        |
除法运算     |      /        |

读取样式

# 获取该文档的所有样式
styles = list(doc.styles)
# 获取第一个样式的名字
print(styles[0].name)
# 获取第一个样式的字体
print(styles[0].font.name)
# 获取第一个样式的字体的大小
print(styles[0].font.size)
Normal
Times New Roman
139700

读取内联图片

python-docx 读取内联图片的方式有点复杂,官方文档好像也没有提及如何提取图片。实际上docx文档是包含了一些xml文件和二进制文件的zip压缩文档。所以,可以通过分析xml的标签或者将doc解压缩来获取图片的信息。实际上,python-docx正是通过解析docx文档中的xml文件来提取其中的信息。

document.xml 文件包含了docx文件的主要内容,图片的信息也在其中。在 document.xml 中,图片的出现顺序和图片在文件中显示的顺序是一致的。每个图片都有一个r:id的标签?可以迭代Documentinline_shapes对象,从而获得其中的每一个inline_shape对象的riD值。

rId = []
for inline_img in doc.inline_shapes:
    # pic.blipFill.blip.embed 这些都是 document.xml 中的标签
    blip = inline_img._inline.graphic.graphicData.pic.blipFill.blip
    rId.append(blip.embed)
print(rId)
['rId19', 'rId20', 'rId24', 'rId25', 'rId27', 'rId28']

通过 Document.part 属性,可以获取储存了document.xml解析结果的DocumentPart对象

document_part = doc.part
print(document_part.partname)
/word/document.xml

DocumentPart对象的属性related_parts是一个字典,将document.xml中的每一个r:id映射到该r:id所在的部分。因此,可以通过内联图片的r:id从属性related_parts中获取图片的xml原始信息。

rid = rId[0]
image_part = document_part.related_parts[rid]
print(f"'{rid}'对应的图片文件名:", image_part.filename)
'rId19'对应的图片文件名: image.png

然而,通过.filename属性获得的文件名似乎不是完全正确的,完整的图片文件名还带有编号的。通过.partname属性,可以看到图片在docx压缩文件中的真实文件名。

print(f"'{rid}'对应的图片文件名:", image_part.partname)
'rId19'对应的图片文件名: /word/media/image1.png

通过.blob属性(blob在计算机领域中,似乎是binary large object的缩写),可以获得图片的二进制数据。

print(type(image_part.blob))
print(len(image_part.blob))
<class 'bytes'>
23329

保存图片的二进制数据,可以得到图片文件

with open(r"datasets/file/image1.png", "wb") as f:
    f.write(image_part.blob)
os.remove(r"datasets/file/image1.png")

或者使用cv2库将二进制的图片数据转换为数组,通过matplotlib可以显示出该图片。

import matplotlib.pyplot as plt
import cv2
import numpy as np

img = cv2.imdecode(np.frombuffer(image_part.blob, np.uint8), cv2.IMREAD_COLOR)
plt.imshow(img)

InlineShape对象还带有图片的长宽信息(但好像不太准):

print(inline_img.height.cm, inline_img.width.cm)
1.7796 10.591322222222223

InlineShape对象还带有一个name标签,但不知道是什么含义:

print(inline_img._inline.graphic.graphicData.pic.nvPicPr.cNvPr.name)
Picture 237

参考

  1. 第105天: Python 操作 Word
  2. 使用样式—— 使用Python读写Office文档之四
  3. python-docx中文
  4. 利用python-docx批量处理Word文件—图片
  5. 【筆記】python docx 抓圖
  6. python 使用cv2、io.BytesIO处理图片二进制数据