【Python】Spire.Doc

第一章:Spire.Doc

1.1 Word文档的本质剖析:Spire.Doc视角下的文件结构与内容表征

要深度理解如何使用Spire.Doc读取Word文档内容,我们首先需要从宏观和微观两个层面,以Spire.Doc的抽象视角,重新审视Word文档的本质。传统的Word文档,尤其是.docx格式,并非单一的二进制文件,而是一个符合Open XML标准(Office Open XML,简称OOXML)的压缩包(实际上是一个ZIP文件)。这个ZIP文件内部包含了大量的XML文件、媒体文件以及其他辅助文件,共同描述了文档的结构、内容、样式和元数据。

Spire.Doc作为一款强大的商业级Word文档处理库,其核心工作就是解析这些复杂的XML结构,并将其映射为一套易于程序员操作的、面向对象的文档对象模型(Document Object Model,简称DOM)。因此,当我们谈论“从最底层开始分析”时,在Spire.Doc的语境下,它意味着深刻理解Spire.Doc自身的DOM如何抽象和表征Word文档的每一个细节,而不是直接去解析原始的XML文件。

1.1.1 OOXML格式的Spire.Doc抽象:.docx文件内部的逻辑映射

一个.docx文件,当通过Spire.Doc加载时,它会执行一系列复杂的内部操作:

  • 解压与解析:Spire.Doc首先会识别并解压.docx文件,读取其内部各个XML部件(Part)。例如,document.xml包含了文档的主体内容,styles.xml定义了样式,header*.xmlfooter*.xml分别包含页眉和页脚内容,_rels/.relsdocProps/*等则存储了关系和文档属性。
  • 构建DOM树:Spire.Doc将这些XML部件中的信息,根据Open XML的规范,以及其自身的内部逻辑,逐层构建为一个层次化的对象模型。这个模型是内存中的一个树状结构,其根节点通常是一个表示整个文档的Document对象。
  • 内容与属性的封装:XML中的各种元素和属性,例如段落(<w:p>)、运行块(<w:r>)、文本(<w:t>)、表格(<w:tbl>)、图片(<w:drawing>)等,都被封装成Spire.Doc中对应的Python对象(如ParagraphTextRangeTableDocPicture等),并暴露出易于访问的属性和方法。

这种抽象的意义在于,开发者无需关心底层的XML语法和复杂的命名空间,可以直接通过Spire.Doc提供的Python API,像操作普通Python对象一样,对Word文档的内容、格式和结构进行读取、修改和创建。

1.1.2 文档内容的逻辑组织:Spire.Doc的DOM层次结构概览

Spire.Doc将Word文档的复杂性归纳为一套清晰的层次结构。理解这套结构是有效读取内容的基石。以下是Spire.Doc中核心对象的初步介绍:

  • Document对象:这是整个Word文档的最高层表示。当你加载一个Word文件时,你首先会得到一个Document实例。它包含了文档的所有其他组成部分,如节、样式、页眉页脚集合等。
  • Section对象:一个Word文档可以包含一个或多个“节”(Section)。每个节可以有独立的页面设置(如页边距、纸张大小、方向)、页眉页脚、列布局等。Document对象内部通常包含一个Sections集合,你可以通过索引或迭代来访问这些节。
  • Body对象:每个Section都有一个Body(主体)部分,它包含了该节的主要文本内容、表格、图片等。这是我们读取文档主要内容时最常交互的对象。
  • Paragraph对象:文档内容最基本的块级元素是段落。无论是普通文本、列表项、标题,还是包含图片、表格的空行,在Spire.Doc中都可能被表示为一个Paragraph对象。Body对象内部通常包含一个Paragraphs集合。
  • TextRange对象:在一个Paragraph内部,文本可能具有不同的格式(例如,同一段落中部分文本是粗体,部分是斜体)。Spire.Doc将这些具有相同格式的连续文本块表示为TextRange(文本范围)对象。一个Paragraph可以包含一个或多个TextRange
  • Table对象:用于表示文档中的表格结构。Body对象或Paragraph对象(如果表格是内联的)可以包含Table对象。Table对象内部又包含TableRow(行)和TableCell(单元格)对象。
  • DocPicture对象:表示文档中的图片。图片可以是内联在文本中的,也可以是浮动在页面上的。
  • Shape对象:表示文档中的各种图形,如文本框、自选图形等。

[公式图片:Spire.Doc DOM结构示意图 - 矩形表示对象,箭头表示包含关系,自上而下:Document -> Section -> Body -> (Paragraph | Table | DocPicture | Shape) -> Paragraph -> TextRange。并列关系:Paragraphs, Tables, Pictures, Shapes 集合]

理解这个层次结构至关重要,因为它指导了我们如何通过代码逐层深入地访问和提取Word文档中的特定内容。例如,要读取一个段落的文本,你通常需要先获取Document对象,然后遍历其Sections,再进入每个SectionBody,最后遍历Body中的Paragraphs,并从Paragraph中获取其包含的TextRanges的文本内容。

1.2 Spire.Doc for Python的安装与基本配置

在深入代码之前,我们必须确保Spire.Doc for Python库已正确安装并可以运行。Spire.Doc for Python是基于.NET实现的Python库,它允许Python开发者利用.NET的强大功能处理Word文档。

1.2.1 安装Spire.Doc for Python

Spire.Doc for Python的安装通常通过pip完成。

# 1. 打开您的命令行终端(Command Prompt, PowerShell, 或 Bash)。
# 这是安装Spire.Doc for Python库的标准命令。
# pip是Python的包管理工具,用于安装和管理Python包。
# Spire.Doc for Python的官方发布包名称通常是'Spire.Doc'。
pip install Spire.Doc

中文解释:

  • # 1. 打开您的命令行终端(Command Prompt, PowerShell, 或 Bash)。:这条注释指导用户首先打开操作系统提供的命令行界面。
  • # 这是安装Spire.Doc for Python库的标准命令。:解释了接下来命令的目的。
  • # pip是Python的包管理工具,用于安装和管理Python包。:对pip的作用进行简要说明。
  • # Spire.Doc for Python的官方发布包名称通常是'Spire.Doc'。:指出要安装的包的准确名称。
  • pip install Spire.Doc:这是实际执行安装的命令,pip会从Python包索引(PyPI)下载并安装Spire.Doc包及其所有依赖项。

重要提示:Spire.Doc是一个商业库。免费版通常会有功能限制(例如,只能处理一定页数或在文档上添加水印)。若要使用完整功能且无限制,您需要购买并申请许可证。在大多数情况下,初学者和测试目的可以使用免费版进行学习。本指南中的代码示例将主要关注如何读取内容,这些基本读取功能在免费版中通常是可用的。

1.2.2 验证安装

安装完成后,您可以通过简单的Python脚本来验证Spire.Doc是否成功安装并可以导入。

# 1. 尝试从Spire.Doc命名空间导入Document类。
# 如果安装成功,这行代码将不会引发任何错误。
from Spire.Doc import Document

# 2. 打印一条成功消息,表明库已成功导入。
# 这只是一个简单的验证步骤,不涉及文档处理。
print("Spire.Doc 库已成功导入!")

# 3. 如果您想进一步验证,可以尝试创建一个空的文档对象并释放它。
# 这是一个更彻底的验证,确保Spire.Doc的核心功能可以被调用。
try:
    # 3.1 创建一个空的Document实例。
    # 这将在内存中初始化一个Word文档对象。
    doc = Document()
    # 3.2 打印一条消息,确认文档对象已创建。
    print("Document 对象已成功创建。")
    # 3.3 释放文档资源。
    # 对于Spire.Doc对象,特别是Document对象,及时释放资源是一个良好的实践。
    # 这有助于回收内存并关闭文件句柄(如果文件被加载过)。
    doc.Close()
    # 3.4 打印消息,确认资源已释放。
    print("Document 对象资源已释放。")
except Exception as e:
    # 3.5 捕获任何可能发生的异常。
    # 如果在导入或创建Document对象时发生错误,将打印错误信息。
    print(f"验证 Spire.Doc 失败:{
     
     e}")

中文解释:

  • from Spire.Doc import Document:从Spire.Doc模块中导入Document类。这是使用Spire.Doc处理Word文档的起点,因为Document类代表了整个Word文档。
  • print("Spire.Doc 库已成功导入!"):如果上述导入语句没有报错,则打印此消息,表示库已在Python环境中可用。
  • try::开始一个try-except块,用于捕获可能在文档操作过程中发生的错误,增强代码的健壮性。
  • doc = Document():创建一个Document类的实例。这相当于在内存中新建一个空的Word文档。
  • print("Document 对象已成功创建。"):如果Document对象创建成功,打印此消息。
  • doc.Close():调用Document对象的Close()方法。这是一个重要的资源管理步骤,用于释放与文档对象关联的所有系统资源,包括内存和可能的文件句柄。
  • print("Document 对象资源已释放。"):确认资源已成功释放。
  • except Exception as e::如果try块中的任何代码抛出异常(例如,Spire.Doc没有正确安装,或者环境问题),则捕获该异常。
  • print(f"验证 Spire.Doc 失败:{e}"):打印捕获到的异常信息,帮助用户诊断问题。
1.3 文件路径处理与编码基础

在进行任何文件操作时,正确处理文件路径和字符编码是至关重要的。特别是在跨操作系统或处理包含非ASCII字符(如中文)的文件名和内容时。

1.3.1 文件路径的规范化与跨平台兼容性

Python的os.path模块和pathlib模块提供了强大的工具来处理文件路径,使其在不同操作系统(Windows, Linux, macOS)上具有兼容性。

import os
from pathlib import Path

# 1. 定义一个Word文档文件名。
# 这是我们要读取的示例文件名。
document_name = "示例文档.docx"

# 2. 定义一个存储文档的目录路径。
# os.getcwd()获取当前工作目录。
# 理论上,您的文档应该放在这个目录下,或者您指定一个绝对路径。
document_directory = os.getcwd()

# 3. 使用os.path.join构建完整的文件路径。
# os.path.join会自动根据操作系统使用正确的路径分隔符(例如Windows上是'\\', Linux/macOS上是'/')。
# 这种方法是跨平台兼容性最好的实践。
full_path_os_join = os.path.join(document_directory, document_name)
# 打印通过os.path.join构建的完整路径。
print(f"使用 os.path.join 构建的路径:{
     
     full_path_os_join}")

# 4. 使用pathlib模块构建完整的文件路径。
# pathlib是Python 3.4+引入的更现代、面向对象的路径处理模块。
# 它提供了更直观的操作符重载(如'/')来连接路径。
path_obj_directory = Path(document_directory)
path_obj_name = Path(document_name)
full_path_pathlib = path_obj_directory / path_obj_name
# 打印通过pathlib构建的完整路径。
print(f"使用 pathlib 构建的路径:{
     
     full_path_pathlib}")

# 5. 将pathlib路径对象转换为字符串,以便Spire.Doc等库使用。
# 许多库仍然期望字符串形式的路径,所以通常需要调用str()。
full_path_string = str(full_path_pathlib)
print(f"Pathlib 转换为字符串的路径:{
     
     full_path_string}")

# 6. 示例:处理绝对路径与相对路径
# 绝对路径:从根目录开始的完整路径,例如 "C:\Users\...\document.docx" 或 "/home/user/.../document.docx"
absolute_path_example = r"D:\webFiles\cursorfiles\my_docs\test_doc.docx" # Windows 示例
# 对于Linux/macOS: "/home/user/documents/test_doc.docx"

# 相对路径:相对于当前工作目录的路径。
# 如果您的Python脚本在 /d/webFiles/cursorfiles 运行,而文档在 /d/webFiles/cursorfiles/docs
relative_path_example = "docs/another_doc.docx"

# Spire.Doc在加载文件时,会根据您提供的路径是绝对路径还是相对路径进行解析。
# 推荐始终使用绝对路径或者确保您的相对路径在程序运行时是正确的。
print(f"绝对路径示例:{
     
     absolute_path_example}")
print(f"相对路径示例:{
     
     relative_path_example}")

中文解释:

  • import os:导入Python的os模块,提供了与操作系统交互的功能,包括文件和目录路径操作。
  • from pathlib import Path:从pathlib模块中导入Path类,这是一个更现代、更面向对象的路径处理工具。
  • document_name = "示例文档.docx":定义一个字符串变量,存储Word文档的文件名。
  • document_directory = os.getcwd():使用os.getcwd()函数获取当前Python脚本的执行目录,并将其赋值给document_directory变量。这是构建相对路径的基础。
  • full_path_os_join = os.path.join(document_directory, document_name):使用os.path.join()函数安全地连接目录路径和文件名。此函数会自动处理不同操作系统下的路径分隔符(例如Windows的\和Linux/macOS的/),确保路径的跨平台兼容性。
  • print(f"使用 os.path.join 构建的路径:{full_path_os_join}"):打印通过os.path.join()构建的完整文件路径。
  • path_obj_directory = Path(document_directory):创建一个Path对象,表示文档所在的目录。pathlib模块将路径视为对象,便于链式操作。
  • path_obj_name = Path(document_name):创建一个Path对象,表示文档的文件名。
  • full_path_pathlib = path_obj_directory / path_obj_name:使用pathlib/运算符连接两个Path对象,这是一种更简洁、直观的路径连接方式,同样具有跨平台特性。
  • print(f"使用 pathlib 构建的路径:{full_path_pathlib}"):打印通过pathlib构建的完整文件路径。
  • full_path_string = str(full_path_pathlib):将pathlibPath对象转换为普通的字符串类型。虽然pathlib对象很方便,但许多第三方库(如Spire.Doc)在接收文件路径参数时,可能仍期望传入一个字符串。
  • print(f"Pathlib 转换为字符串的路径:{full_path_string}"):打印转换后的字符串路径。
  • absolute_path_example = r"D:\webFiles\cursorfiles\my_docs\test_doc.docx":定义一个Windows系统下的原始字符串形式的绝对路径示例。r前缀表示这是一个原始字符串(raw string),避免了反斜杠\被解释为转义字符。
  • relative_path_example = "docs/another_doc.docx":定义一个相对路径示例。这意味着文档位于当前执行脚本目录下的docs子目录中。
  • print(f"绝对路径示例:{absolute_path_example}"):打印绝对路径示例。
  • print(f"相对路径示例:{relative_path_example}"):打印相对路径示例。
1.3.2 字符编码的重要性

当处理Word文档中的文本内容时,尤其涉及到非英文字符(如中文、日文、韩文等),字符编码是一个必须考虑的因素。Word文档内部的文本通常是以UTF-8编码存储在XML文件中的。Spire.Doc在读取时会负责解析这些编码,并将其转换为Python的Unicode字符串。

一般情况下,Spire.Doc会自动处理大部分编码问题。但是,当你在将读取到的文本写入到其他文件(如TXT文件、CSV文件)时,或者在命令行中显示文本时,就需要注意输出编码。

# 1. 假设我们从Word文档中读取到以下包含中文的文本。
# 在Python 3中,字符串默认是Unicode,可以很好地处理中文。
text_from_word = "这是一个从Word文档中读取的中文示例文本。"
print(f"原始文本(Python Unicode):{
     
     text_from_word}")

# 2. 将文本写入一个外部文件,明确指定编码。
output_file_name = "output_text.txt"

try:
    # 2.1 以UTF-8编码写入文件。
    # UTF-8是目前最广泛使用的Unicode编码,支持所有语言字符,且兼容ASCII。
    # 'w'模式表示写入(如果文件存在则覆盖),'t'模式表示文本模式。
    # encoding='utf-8'明确指定了写入文件的编码格式。
    with open(output_file_name, 'wt', encoding='utf-8') as f:
        # 2.2 将读取到的文本内容写入文件。
        f.write(text_from_word)
    # 2.3 打印成功消息。
    print(f"文本已成功写入 '{
     
     output_file_name}',编码为 UTF-8。")

    # 2.4 再次读取文件以验证编码。
    # 以UTF-8编码读取文件,确保正确解码。
    with open(output_file_name, 'rt', encoding='utf-8') as f:
        read_text = f.read()
    # 2.5 打印读取到的文本。
    print(f"从文件读取的文本(验证):{
     
     read_text}")

except Exception as e:
    # 2.6 捕获写入或读取文件时可能发生的错误。
    print(f"文件操作发生错误:{
     
     e}")

# 3. 在命令行中显示中文文本。
# 确保您的命令行终端支持UTF-8编码。
# Windows用户可能需要将终端的编码设置为UTF-8 (chcp 65001)。
print("\n在命令行中显示中文文本:")
print(text_from_word)

中文解释:

  • text_from_word = "这是一个从Word文档中读取的中文示例文本。":定义一个包含中文字符的字符串变量,模拟从Word文档中读取到的文本。在Python 3中,字符串默认是Unicode编码,可以很好地处理各种语言字符。
  • output_file_name = "output_text.txt":定义要写入的文本文件的名称。
  • try::开始一个try-except块,用于处理文件操作可能出现的异常,如权限不足或编码错误。
  • with open(output_file_name, 'wt', encoding='utf-8') as f::使用with语句打开一个文件,确保文件在使用完毕后会被正确关闭。
    • output_file_name:要打开的文件名。
    • 'wt':文件打开模式。'w'表示写入模式(如果文件不存在则创建,如果存在则清空内容),'t'表示文本模式。
    • encoding='utf-8'明确指定文件编码为UTF-8。这是处理中文或其他非ASCII字符的关键,可以避免乱码问题。
  • f.write(text_from_word):将text_from_word变量中的文本内容写入到打开的文件f中。
  • print(f"文本已成功写入 '{output_file_name}',编码为 UTF-8。"):打印成功写入文件的提示信息。
  • with open(output_file_name, 'rt', encoding='utf-8') as f::再次打开同一个文件,但这次是读取模式('r'),同样指定't'为文本模式,并使用encoding='utf-8'进行解码。
  • read_text = f.read():读取文件的所有内容到read_text变量中。
  • print(f"从文件读取的文本(验证):{read_text}"):打印从文件读取的文本,用于验证写入和读取过程中的编码是否一致且正确。
  • except Exception as e::捕获在try块中发生的任何异常。
  • print(f"文件操作发生错误:{e}"):打印具体的错误信息。
  • print("\n在命令行中显示中文文本:"):提示接下来要在命令行中显示中文。
  • print(text_from_word):直接打印包含中文的字符串。在支持UTF-8的终端中,这将正确显示中文。

这些基础知识构成了使用Spire.Doc进行高效、稳定Word文档内容读取的坚实基础。接下来,我们将深入Spire.Doc的核心对象模型,并开始编写实际的代码来读取文档内容。


第二章:Spire.Doc文档对象模型(DOM)的深层解析

本章将对Spire.Doc的核心文档对象模型(DOM)进行深入解析,这是理解和高效操作Word文档内容的关键。我们将从最顶层的Document对象开始,逐步向下探索其包含的各个核心组成部分,并详细解释它们在Word文档逻辑结构中的对应关系以及在Spire.Doc中的属性和方法。

2.1 Document对象:Word文档的根与全局控制器

在Spire.Doc中,Document对象是Word文档的最高级表示。它是所有其他文档元素(如节、段落、表格、图片、样式、页眉页脚等)的容器。当你加载一个Word文档文件时,你首先会得到一个Document类的实例。

2.1.1 Document对象的创建与加载

Document对象可以被创建为一个全新的空文档,也可以通过加载现有文件来实例化。

from Spire.Doc import Document
from Spire.Doc.FileFormat import FileFormat
import os

# 1. 创建一个全新的、空的Word文档。
# 这是一个非常基础的Document对象初始化,代表一个空白的Word文档。
# 在内存中创建一个新的文档模型,尚未与任何文件关联。
new_doc = Document()
# 打印一条消息,确认空文档已创建。
print("已成功创建一个新的空Word文档。")

# 2. 加载一个现有的Word文档。
# 假设我们有一个名为 "MySampleDoc.docx" 的Word文件。
# 为了演示,我们先创建一个空的文档并保存,以便后续加载。
sample_doc_path = os.path.join(os.getcwd(), "MySampleDoc.docx")

try:
    # 2.1 创建一个临时文档,并向其中添加一些内容。
    # 这一步是为了确保有一个可供加载的文档文件。
    temp_doc = Document()
    # temp_doc.AddSection()方法用于向文档添加一个新的节。
    # 节是Word文档中重要的结构单元,可以有独立的页面设置。
    section = temp_doc.AddSection()
    # section.AddParagraph()方法用于在当前节中添加一个新的段落。
    # 段落是Word文档中最基本的文本块。
    paragraph = section.AddParagraph()
    # paragraph.AppendText()方法用于向段落中追加文本内容。
    paragraph.AppendText("这是一个用于演示加载的示例文档。")
    
    # 2.2 将临时文档保存到指定路径。
    # SaveToFile方法将内存中的Document对象保存为物理文件。
    # FileFormat.Docx表示保存为.docx格式。
    temp_doc.SaveToFile(sample_doc_path, FileFormat.Docx)
    # 2.3 打印保存成功消息。
    print(f"已创建并保存临时文档:{
     
     sample_doc_path}")
    # 2.4 关闭临时文档,释放资源。
    temp_doc.Close()

    # 2.5 使用Document类的构造函数加载现有的Word文档。
    # Spire.Doc会自动检测文件格式。
    loaded_doc = Document(sample_doc_path)
    # 2.6 打印加载成功消息。
    print(f"已成功加载文档:{
     
     sample_doc_path}")
    
    # 2.7 访问加载文档的一些基本属性进行验证。
    # loaded_doc.Sections属性返回一个包含文档所有节的集合。
    # len()函数用于获取集合中元素的数量。
    print(f"加载的文档包含 {
     
     len(loaded_doc.Sections)} 个节。")

    # 2.8 处理完文档后,务必关闭Document对象,释放系统资源。
    # 这是一个非常重要的步骤,尤其是在处理大量文档或长时间运行的应用程序中。
    loaded_doc.Close()
    # 2.9 打印关闭消息。
    print(f"已关闭加载的文档:{
     
     sample_doc_path}")

except Exception as e:
    # 捕获并打印在文件操作或Spire.Doc操作过程中发生的任何异常。
    print(f"操作失败:{
     
     e}")
finally:
    # 确保在任何情况下都尝试删除临时文件,保持环境整洁。
    if os.path.exists(sample_doc_path):
        os.remove(sample_doc_path)
        print(f"已删除临时文档:{
     
     sample_doc_path}")

中文解释:

  • from Spire.Doc import Document:导入Spire.Doc库中的Document类,它是操作Word文档的入口。
  • from Spire.Doc.FileFormat import FileFormat:导入FileFormat枚举,用于指定保存文档时的文件格式(如DOCX, DOC, PDF等)。
  • import os:导入Python的os模块,用于操作系统级别的路径和文件操作。
  • new_doc = Document():调用Document类的无参构造函数,在内存中创建一个全新的、空白的Word文档对象。
  • print("已成功创建一个新的空Word文档。"):确认空文档对象已创建。
  • sample_doc_path = os.path.join(os.getcwd(), "MySampleDoc.docx"):构建一个临时的Word文档文件路径,位于当前工作目录下。
  • try::开始一个try-except-finally块,用于处理可能发生的错误,并确保清理工作(删除临时文件)无论如何都会执行。
  • temp_doc = Document():创建另一个临时Document对象,用于生成一个实际的Word文件。
  • section = temp_doc.AddSection():在temp_doc中添加一个新节。每个Word文档至少包含一个节。
  • paragraph = section.AddParagraph():在刚创建的节中添加一个新段落。段落是文本、图片、表格等内容的容器。
  • paragraph.AppendText("这是一个用于演示加载的示例文档。"):向新创建的段落中添加一些示例文本。
  • temp_doc.SaveToFile(sample_doc_path, FileFormat.Docx):将temp_doc对象保存为名为MySampleDoc.docx的物理文件,格式为.docx
  • print(f"已创建并保存临时文档:{sample_doc_path}"):打印保存成功的消息。
  • temp_doc.Close():关闭temp_doc对象,释放其占用的内存和文件句柄。这是使用Spire.Doc的重要习惯。
  • loaded_doc = Document(sample_doc_path):使用Document类的带参数构造函数,通过文件路径加载一个现有的Word文档。Spire.Doc会解析文件内容并将其映射到loaded_doc对象中。
  • print(f"已成功加载文档:{sample_doc_path}"):打印加载成功的消息。
  • print(f"加载的文档包含 {len(loaded_doc.Sections)} 个节。"):访问loaded_docSections属性(它是一个集合,包含文档中的所有节),并使用len()函数获取节的数量,以此验证文档内容已被加载。
  • loaded_doc.Close():关闭loaded_doc对象,释放资源。
  • print(f"已关闭加载的文档:{sample_doc_path}"):打印关闭消息。
  • except Exception as e::捕获并打印在try块中可能发生的任何异常。
  • finally:finally块中的代码无论是否发生异常都会执行。
  • if os.path.exists(sample_doc_path)::检查临时文件是否存在。
  • os.remove(sample_doc_path):如果文件存在,则删除它,保持文件系统的清洁。
  • print(f"已删除临时文档:{sample_doc_path}"):打印删除成功的消息。
2.1.2 Document对象的全局属性与集合

Document对象除了是文档的根,还提供了访问文档全局信息和主要内容集合的入口。

属性名称 类型 描述
Sections SectionCollection 一个集合,包含文档中的所有Section(节)对象。每个节可以有独立的页面设置、页眉页脚等。这是访问文档主体内容(段落、表格、图片等)的主要入口。
Styles StyleCollection 一个集合,包含文档中定义的所有样式(如段落样式、字符样式、表格样式等)。通过此集合可以读取和管理文档的样式信息。
BuiltinDocumentProperties BuiltinDocumentProperties 包含文档的内置属性,如作者、标题、主题、创建日期、修改日期、修订次数等。这些是Word自动维护的元数据。
CustomDocumentProperties CustomDocumentProperties 包含文档的自定义属性。用户可以在Word中添加自己的键值对属性,这些属性可以通过此集合访问。
Bookmarks BookmarkCollection 一个集合,包含文档中定义的所有书签。书签是文档中的一个命名位置,常用于导航或提取特定内容。
Paragraphs ParagraphCollection (全局) 注意:虽然Document对象本身没有直接的ParagraphCollection属性,但其内部的每个SectionBody对象都有Paragraphs集合。此处列出是为了强调段落的重要性,通常通过遍历SectionsBody来访问。
Tables TableCollection (全局) 注意:同上,Document对象没有直接的Tables属性。表格通常位于BodyTableCell内,通过遍历SectionsBody来访问。
ParagraphStyles ParagraphStyleCollection 这是一个更细分的样式集合,专注于段落样式。
CharacterStyles CharacterStyleCollection 这是一个更细分的样式集合,专注于字符样式。
ListStyles ListStyleCollection 包含文档中定义的列表样式(编号、项目符号等)。
Sections.Count int 获取文档中节的数量。
IsEncrypted bool 指示文档是否已加密。
HasMacros bool 指示文档是否包含宏。
ViewSetup ViewSetup 获取或设置文档的视图设置(如缩放比例、显示模式)。
ProtectionType DocumentProtectionType (枚举) 获取文档的保护类型(如只读、批注、表单等)。

下面通过代码示例来演示如何访问这些全局属性:

from Spire.Doc import Document
from Spire.Doc.FileFormat import FileFormat
from Spire.Doc.BuiltinDocumentProperties import BuiltinDocumentProperties
from Spire.Doc.DocumentProtectionType import DocumentProtectionType
import os
import datetime

# 1. 准备一个包含多种特性的示例文档。
# 为了充分演示Document对象的各种属性,我们需要一个稍微复杂点的文档。
# 我们将创建一个新的文档,并添加一些内置属性、自定义属性、节等。
sample_doc_path_for_props = os.path.join(os.getcwd(), "DocumentPropertiesDemo.docx")

try:
    # 1.1 创建一个新文档。
    doc_with_props = Document()
    
    # 1.2 设置内置文档属性。
    # doc_with_props.BuiltinDocumentProperties属性返回一个BuiltinDocumentProperties对象。
    # 我们可以通过这个对象的属性直接设置内置文档信息,如作者、标题等。
    doc_with_props.BuiltinDocumentProperties.Author = "文档作者:深入Python" # 设置作者
    doc_with_props.BuiltinDocumentProperties.Title = "Python Spire.Doc文档属性演示" # 设置标题
    doc_with_props.BuiltinDocumentProperties.Subject = "Spire.Doc文档对象属性的极致讲解" # 设置主题
    doc_with_props.BuiltinDocumentProperties.Comments = "这是一个用于演示Spire.Doc Document对象属性的详尽示例文档。" # 设置注释
    # doc_with_props.BuiltinDocumentProperties.LastPrinted = datetime.datetime.now() # 设置最后打印时间
    # 这里的LastPrinted等日期时间属性在某些Spire.Doc版本中可能需要手动格式化或直接设置DateTime对象。
    # 为了简化演示,我们聚焦于字符串属性。
    
    # 1.3 添加自定义文档属性。
    # doc_with_props.CustomDocumentProperties属性返回一个CustomDocumentProperties集合。
    # Add方法用于添加自定义属性,需要提供属性名、类型和值。
    doc_with_props.CustomDocumentProperties.Add("CustomKey1", "CustomValue1") # 添加一个字符串类型的自定义属性
    doc_with_props.CustomDocumentProperties.Add("版本号", "1.0.0") # 添加另一个中文名称的自定义属性
    doc_with_props.CustomDocumentProperties.Add("是否保密", True) # 添加一个布尔类型的自定义属性
    
    # 1.4 添加多个节以演示Sections属性。
    # AddSection()方法每调用一次就会在文档末尾添加一个新节。
    # 每个节可以包含独立的页眉、页脚、页面设置等。
    section1 = doc_with_props.AddSection()
    section1.AddParagraph().AppendText("这是文档的第一个节。")
    section2 = doc_with_props.AddSection()
    section2.AddParagraph().AppendText("这是文档的第二个节。")

    # 1.5 尝试设置文档保护(只读类型),演示ProtectionType。
    # Protect方法用于保护文档,第一个参数是保护类型,第二个参数是密码(如果需要)。
    # DocumentProtectionType.AllowOnlyReading表示只允许阅读,不允许修改。
    # "Password123"是设置的密码。
    doc_with_props.Protect(DocumentProtectionType.AllowOnlyReading, "Password123")
    
    # 1.6 保存文档。
    doc_with_props.SaveToFile(sample_doc_path_for_props, FileFormat.Docx)
    doc_with_props.Close() # 保存后关闭
    print(f"已创建并保存包含属性的示例文档:{
     
     sample_doc_path_for_props}")

    # 1.7 重新加载文档,以便读取其属性。
    loaded_doc_for_props = Document(sample_doc_path_for_props)
    print(f"\n--- 正在读取文档属性 ---")

    # 1.8 读取内置文档属性。
    # 获取BuiltinDocumentProperties对象。
    builtin_props = loaded_doc_for_props.BuiltinDocumentProperties
    print(f"作者: {
     
     builtin_props.Author}") # 读取作者属性
    print(f"标题: {
     
     builtin_props.Title}") # 读取标题属性
    print(f"主题: {
     
     builtin_props.Subject}") # 读取主题属性
    print(f"注释: {
     
     builtin_props.Comments}") # 读取注释属性
    print(f"修订次数: {
     
     builtin_props.RevisionNumber}") # 读取修订次数属性(Word自动更新)
    
    # 1.9 读取自定义文档属性。
    # 遍历CustomDocumentProperties集合中的每一个自定义属性。
    print("\n自定义文档属性:")
    # loaded_doc_for_props.CustomDocumentProperties是一个可迭代对象,可以进行遍历。
    for prop in loaded_doc_for_props.CustomDocumentProperties:
        # prop.Name获取自定义属性的名称。
        # prop.Value获取自定义属性的值。
        # prop.Type获取自定义属性的数据类型。
        print(f"  名称: {
     
     prop.Name}, 值: {
     
     prop.Value}, 类型: {
     
     prop.Type}")

    # 1.10 读取节的数量。
    # len(loaded_doc_for_props.Sections)获取文档中节的数量。
    print(f"\n文档包含 {
     
     len(loaded_doc_for_props.Sections)} 个节。")

    # 1.11 检查文档是否加密或受保护。
    # loaded_doc_for_props.IsEncrypted检查文档是否加密。
    # loaded_doc_for_props.ProtectionType获取文档的保护类型。
    print(f"文档是否加密: {
     
     loaded_doc_for_props.IsEncrypted}")
    print(f"文档保护类型: {
     
     loaded_doc_for_props.ProtectionType}") # 返回DocumentProtectionType枚举值
    # 比较保护类型是否为只读。
    print(f"文档是否为只读保护: {
     
     loaded_doc_for_props.ProtectionType == DocumentProtectionType.AllowOnlyReading}")

    # 1.12 检查是否包含宏。
    print(f"文档是否包含宏: {
     
     loaded_doc_for_props.HasMacros}") # 示例文档未添加宏,通常为False

    # 1.13 关闭文档。
    loaded_doc_for_props.Close()
    print(f"已关闭文档:{
     
     sample_doc_path_for_props}")

except Exception as e:
    print(f"发生错误:{
     
     e}")
finally:
    # 清理临时文件。
    if os.path.exists(sample_doc_path_for_props):
        os.remove(sample_doc_path_for_props)
        print(f"已删除临时文档:{
     
     sample_doc_path_for_props}")

中文解释:

  • from Spire.Doc import Document, from Spire.Doc.FileFormat import FileFormat, from Spire.Doc.BuiltinDocumentProperties import BuiltinDocumentProperties, from Spire.Doc.DocumentProtectionType import DocumentProtectionType, import os, import datetime:导入所需的类和模块。BuiltinDocumentProperties用于访问内置属性,DocumentProtectionType是枚举,用于文档保护类型。
  • sample_doc_path_for_props = os.path.join(os.getcwd(), "DocumentPropertiesDemo.docx"):定义一个用于演示的Word文档路径。
  • try::开始try-except-finally块。
  • doc_with_props = Document():创建一个新的空白Document对象。
  • doc_with_props.BuiltinDocumentProperties.Author = "文档作者:深入Python":通过BuiltinDocumentProperties属性访问并设置文档的内置属性,如作者。
  • doc_with_props.BuiltinDocumentProperties.Title = "Python Spire.Doc文档属性演示":设置文档标题。
  • doc_with_props.BuiltinDocumentProperties.Subject = "Spire.Doc文档对象属性的极致讲解":设置文档主题。
  • doc_with_props.BuiltinDocumentProperties.Comments = "这是一个用于演示Spire.Doc Document对象属性的详尽示例文档。":设置文档注释。
  • doc_with_props.CustomDocumentProperties.Add("CustomKey1", "CustomValue1"):通过CustomDocumentProperties集合的Add方法添加自定义文档属性,包括名称和值。
  • doc_with_props.CustomDocumentProperties.Add("版本号", "1.0.0"):添加另一个自定义属性,其名称包含中文。
  • doc_with_props.CustomDocumentProperties.Add("是否保密", True):添加一个布尔类型的自定义属性。
  • section1 = doc_with_props.AddSection():在文档中添加第一个节。
  • section1.AddParagraph().AppendText("这是文档的第一个节。"):在第一个节中添加一个段落和文本。
  • section2 = doc_with_props.AddSection():添加第二个节。
  • section2.AddParagraph().AppendText("这是文档的第二个节。"):在第二个节中添加一个段落和文本。
  • doc_with_props.Protect(DocumentProtectionType.AllowOnlyReading, "Password123"):调用Protect方法设置文档保护。DocumentProtectionType.AllowOnlyReading表示只读保护,"Password123"是解除保护所需的密码。
  • doc_with_props.SaveToFile(sample_doc_path_for_props, FileFormat.Docx):将文档保存为.docx文件。
  • doc_with_props.Close():关闭文档对象。
  • loaded_doc_for_props = Document(sample_doc_path_for_props):重新加载刚才保存的文档,以便读取其属性。
  • builtin_props = loaded_doc_for_props.BuiltinDocumentProperties:获取加载文档的内置属性对象。
  • print(f"作者: {builtin_props.Author}"):打印读取到的作者信息。
  • print(f"标题: {builtin_props.Title}"):打印读取到的标题信息。
  • print(f"主题: {builtin_props.Subject}"):打印读取到的主题信息。
  • print(f"注释: {builtin_props.Comments}"):打印读取到的注释信息。
  • print(f"修订次数: {builtin_props.RevisionNumber}"):打印读取到的修订次数。
  • for prop in loaded_doc_for_props.CustomDocumentProperties::遍历CustomDocumentProperties集合,访问每一个自定义属性。
  • print(f" 名称: {prop.Name}, 值: {prop.Value}, 类型: {prop.Type}"):打印自定义属性的名称、值和类型。
  • print(f"\n文档包含 {len(loaded_doc_for_props.Sections)} 个节。"):打印文档中节的数量,通过访问Sections集合的长度。
  • print(f"文档是否加密: {loaded_doc_for_props.IsEncrypted}"):检查并打印文档是否加密的布尔值。
  • print(f"文档保护类型: {loaded_doc_for_props.ProtectionType}"):打印文档的保护类型,返回DocumentProtectionType枚举成员。
  • print(f"文档是否为只读保护: {loaded_doc_for_props.ProtectionType == DocumentProtectionType.AllowOnlyReading}"):判断文档是否是只读保护类型。
  • print(f"文档是否包含宏: {loaded_doc_for_props.HasMacros}"):检查并打印文档是否包含宏。
  • loaded_doc_for_props.Close():关闭加载的文档对象。
  • finally:块用于确保os.remove(sample_doc_path_for_props):删除在演示过程中创建的临时Word文档,保持环境整洁。

这个示例详细展示了如何通过Document对象访问和读取Word文档的全局元数据和宏观结构信息。

2.2 Section对象:文档结构的基本单元

Section(节)是Word文档中一个非常重要的结构化概念。一个Word文档可以包含一个或多个节。每个节都可以拥有自己独立的页面布局设置(如纸张大小、方向、页边距)、页眉页脚、行号、列设置等。Spire.Doc中的Section对象精确地映射了Word文档中的这一概念。

2.2.1 Section对象的获取与遍历

你可以通过Document对象的Sections集合来访问文档中的所有节。Sections集合是一个可迭代对象,支持索引访问和for循环遍历。

from Spire.Doc import Document
from Spire.Doc.FileFormat import FileFormat
import os

# 1. 创建一个多节的示例文档。
multi_section_doc_path = os.path.join(os.getcwd(), "MultiSectionDoc.docx")

try:
    # 1.1 创建新文档。
    doc_sections = Document()
    
    # 1.2 添加第一个节并写入内容。
    section1_write = doc_sections.AddSection()
    section1_write.AddParagraph().AppendText("这是文档中的第一个节的内容。")
    section1_write.AddParagraph().AppendText("它包含一些示例文本。")
    # 设置第一个节的页面方向为横向。
    # section1_write.PageSetup属性返回一个PageSetup对象,用于设置页面布局。
    # Orientation属性可以设置为PageOrientation.Landscape(横向)或PageOrientation.Portrait(纵向)。
    from Spire.Doc.PageOrientation import PageOrientation
    section1_write.PageSetup.Orientation = PageOrientation.Landscape
    
    # 1.3 添加第二个节并写入内容。
    section2_write = doc_sections.AddSection()
    section2_write.AddParagraph().AppendText("这是文档中的第二个节的内容。")
    section2_write.AddParagraph().AppendText("这个节有独立的页面设置。")
    # 设置第二个节的页面方向为纵向(默认通常是纵向,这里明确设置)。
    section2_write.PageSetup.Orientation = PageOrientation.Portrait
    
    # 1.4 添加第三个节并写入内容。
    section3_write = doc_sections.AddSection()
    section3_write.AddParagraph().AppendText("这是文档中的第三个节的内容。")
    section3_write.AddParagraph().AppendText("每个节都是一个独立的逻辑区域。")

    # 1.5 保存文档。
    doc_sections.SaveToFile(multi_section_doc_path, FileFormat.Docx)
    doc_sections.Close()
    print(f"已创建并保存多节文档:{
     
     multi_section_doc_path}")

    # 1.6 重新加载文档,以便读取节信息。
    loaded_doc_sections = Document(multi_section_doc_path)
    print(f"\n--- 正在遍历文档中的节 ---")

    # 1.7 获取文档中节的总数量。
    total_sections = loaded_doc_sections.Sections.Count
    print(f"文档总共有 {
     
     total_sections} 个节。")

    # 1.8 遍历文档中的所有节。
    # loaded_doc_sections.Sections是一个集合,可以通过for循环直接遍历其中的Section对象。
    for i, section in enumerate(loaded_doc_sections.Sections):
        # 1.8.1 打印当前节的索引(从0开始)和信息。
        print(f"\n--- 第 {
     
     i+1} 个节 ---")
        
        # 1.8.2 读取节的页面设置属性。
        # section.PageSetup属性返回当前节的页面设置对象。
        # Orientation属性指示页面的方向。
        # TextDirection属性指示文本的书写方向。
        # PaperSize属性指示纸张大小。
        print(f"  页面方向: {
     
     section.PageSetup.Orientation}")
        print(f"  文本方向: {
     
     section.PageSetup.TextDirection}")
        print(f"  纸张大小: {
     
     section.PageSetup.PaperSize}")

        # 1.8.3 读取节的主体内容(Body)中的段落数量。
        # section.Body属性是当前节的主体内容容器。
        # Body.Paragraphs是Body中所有段落的集合。
        print(f"  包含段落数量: {
     
     section.Body.Paragraphs.Count}")
        
        # 1.8.4 简单读取节的第一个段落内容进行验证。
        # 确保Body.Paragraphs不为空,以避免索引越界错误。
        if section.Body.Paragraphs.Count > 0:
            # section.Body.Paragraphs[0]通过索引获取第一个段落。
            # Text属性获取段落的纯文本内容。
            first_paragraph_text = section.Body.Paragraphs[0].Text
            # 限制打印长度,避免内容过长影响阅读。
            print(f"  第一个段落内容(截取前50字): {
     
     first_paragraph_text[:50]}...")
        else:
            print(f"  此节不包含任何段落。")

    # 1.9 关闭文档。
    loaded_doc_sections.Close()
    print(f"已关闭文档:{
     
     multi_section_doc_path}")

except Exception as e:
    print(f"发生错误:{
     
     e}")
finally:
    if os.path.exists(multi_section_doc_path):
        os.remove(multi_section_doc_path)
        print(f"已删除临时文档:{
     
     multi_section_doc_path}")

中文解释:

  • from Spire.Doc import Document, from Spire.Doc.FileFormat import FileFormat, from Spire.Doc.PageOrientation import PageOrientation, import os:导入所需模块和枚举。PageOrientation用于设置页面方向。
  • multi_section_doc_path = os.path.join(os.getcwd(), "MultiSectionDoc.docx"):定义一个用于演示多节文档的文件路径。
  • doc_sections = Document():创建一个新的空白文档。
  • section1_write = doc_sections.AddSection():通过Document对象的AddSection()方法添加一个新节。
  • section1_write.AddParagraph().AppendText("这是文档中的第一个节的内容。"):在第一个节中添加一个段落并写入文本。
  • section1_write.PageSetup.Orientation = PageOrientation.Landscape:访问Section对象的PageSetup属性,并设置其OrientationPageOrientation.Landscape,即横向。
  • section2_write = doc_sections.AddSection():添加第二个节。
  • section2_write.PageSetup.Orientation = PageOrientation.Portrait:设置第二个节的页面方向为纵向。
  • section3_write = doc_sections.AddSection():添加第三个节。
  • doc_sections.SaveToFile(multi_section_doc_path, FileFormat.Docx):保存多节文档。
  • doc_sections.Close():关闭文档。
  • loaded_doc_sections = Document(multi_section_doc_path):重新加载保存的文档。
  • total_sections = loaded_doc_sections.Sections.Count:通过Sections集合的Count属性获取节的总数量。
  • for i, section in enumerate(loaded_doc_sections.Sections)::使用enumerate函数遍历loaded_doc_sections.Sections集合。i是当前节的索引,section是当前的Section对象。
  • print(f"\n--- 第 {i+1} 个节 ---"):打印当前节的序号。
  • print(f" 页面方向: {section.PageSetup.Orientation}"):打印当前节的页面方向。
  • print(f" 文本方向: {section.PageSetup.TextDirection}"):打印当前节的文本方向。
  • print(f" 纸张大小: {section.PageSetup.PaperSize}"):打印当前节的纸张大小。
  • print(f" 包含段落数量: {section.Body.Paragraphs.Count}"):打印当前节主体(Body)中包含的段落数量。BodySection的一个属性,它代表了节的主要内容区域。
  • if section.Body.Paragraphs.Count > 0::检查当前节是否包含段落,以避免对空集合进行索引操作。
  • first_paragraph_text = section.Body.Paragraphs[0].Text:获取当前节的第一个段落的纯文本内容。
  • print(f" 第一个段落内容(截取前50字): {first_paragraph_text[:50]}..."):打印第一个段落的前50个字符。
  • loaded_doc_sections.Close():关闭文档。
  • finally:块用于os.remove(multi_section_doc_path):删除临时文件。
2.2.2 Section对象的关键属性
属性名称 类型 描述
Body Body 节的主体内容。这是最重要的属性,包含了节内所有的段落、表格、图片、图形等。你需要通过Body对象来访问节的实际内容。
HeadersFooters HeadersFooters 节的页眉页脚集合。一个节可以有不同的页眉页脚(首页、奇偶页等)。通过此属性可以访问和修改页眉页脚的内容。
PageSetup PageSetup 节的页面设置。包含了纸张大小、方向、页边距、页码起始等所有页面布局相关的属性。
BreakCode SectionBreakType (枚举) 获取或设置节前中断类型。例如,新页、新列、连续等。
Columns ColumnCollection 获取节中的列设置,例如多列布局。
Footnotes FootnoteCollection 节中的所有脚注。
Endnotes EndnoteCollection 节中的所有尾注。
Protect 方法 为节设置保护。
AllowFormatting bool 获取或设置是否允许在此节中进行格式设置(当文档受保护时)。
2.3 Body对象:节内容的实体容器

Body对象是Section对象的核心组成部分,它代表了Word文档中一个节的主要内容区域。几乎所有可见的文档内容,如段落、表格、图片、图形等,都直接或间接包含在Body对象中。

2.3.1 Body对象的内容集合

Body对象本身并不直接包含文本,而是包含了一系列表示文档元素的集合。这些集合是我们在提取文档内容时最常交互的。

集合名称 类型 描述
ChildObjects DocumentObjectCollection 一个通用集合,包含了Body中所有顶级子对象。这包括ParagraphTableShape等。这是最底层的访问方式,可以迭代所有内容块。
Paragraphs ParagraphCollection 一个专门的集合,包含了Body中所有Paragraph对象。这是获取纯文本内容(非表格、非图片)最常用的集合。
Tables TableCollection 一个专门的集合,包含了Body中所有Table对象。这是获取表格内容的主要入口。
Shapes ShapeCollection 一个专门的集合,包含了Body中所有Shape对象(如文本框、自选图形、图片等)。
Images DocPictureCollection (通过迭代ShapesChildObjects获取) 虽然没有直接的Images集合,但DocPicture对象通常作为Shape或内联对象存在于ChildObjectsParagraph的子对象中。需要向下转型才能访问。

重要概念:ChildObjects与特定集合的关系

Body中的ChildObjects集合是通用的。它包含了Body直接下的所有IDocumentObject(接口类型)的实例。ParagraphTableShape等都是实现了IDocumentObject接口的具体类。这意味着,你可以遍历ChildObjects,然后根据每个对象的实际类型(使用isinstance或Spire.Doc提供的DocumentObjectType枚举)来执行相应的操作。

ParagraphsTables集合则是便利的筛选器。它们只包含了特定类型的对象。通常,使用这些特定集合更简洁,但如果你需要处理文档中所有不同类型的顶级内容块而不关心其具体顺序,ChildObjects就很有用。

2.3.2 遍历Body内容:以获取所有段落和表格为例

下面我们将通过一个示例,演示如何访问Body对象,并遍历其中的ParagraphsTables集合来提取内容。

from Spire.Doc import Document
from Spire.Doc.FileFormat import FileFormat
from Spire.Doc.Documents import Paragraph, Table
import os

# 1. 准备一个包含段落和表格的示例文档。
doc_with_body_content_path = os.path.join(os.getcwd(), "BodyContentDemo.docx")

try:
    # 1.1 创建一个新文档。
    doc_body = Document()
    section = doc_body.AddSection() # 添加一个节

    # 1.2 在Body中添加普通段落。
    paragraph1 = section.AddParagraph()
    paragraph1.AppendText("这是文档主体中的第一个普通段落。")
    paragraph2 = section.AddParagraph()
    paragraph2.AppendText("这个段落是第二个,紧随其后。")

    # 1.3 在Body中添加一个表格。
    # section.AddTable()方法用于在当前节中添加一个表格。
    # 参数为行数和列数。
    table = section.AddTable()
    # table.ResetCells(行数, 列数)方法用于初始化表格的单元格结构。
    table.ResetCells(3, 3) # 创建一个3行3列的表格
    
    # 设置表格标题和内容。
    # table.Rows属性是一个TableRowCollection,包含表格的所有行。
    # table.Rows[0]访问第一行。
    # table.Rows[0].Cells属性是一个TableCellCollection,包含当前行的所有单元格。
    # table.Rows[0].Cells[0]访问第一行的第一个单元格。
    # AddParagraph()方法在单元格内添加一个段落。
    # AppendText()方法向段落中添加文本。
    table.Rows[0].Cells[0].AddParagraph().AppendText("表头1")
    table.Rows[0].Cells[1].AddParagraph().AppendText("表头2")
    table.Rows[0].Cells[2].AddParagraph().AppendText("表头3")

    table.Rows[1].Cells[0].AddParagraph().AppendText("数据A1")
    table.Rows[1].Cells[1].AddParagraph().AppendText("数据B1")
    table.Rows[1].Cells[2].AddParagraph().AppendText("数据C1")

    table.Rows[2].Cells[0].AddParagraph().AppendText("数据A2")
    table.Rows[2].Cells[1].AddParagraph().AppendText("数据B2")
    table.Rows[2].Cells[2].AddParagraph().AppendText("数据C2")

    # 1.4 在表格后添加另一个段落。
    paragraph3 = section.AddParagraph()
    paragraph3.AppendText("这是表格后的一个段落。")

    # 1.5 保存文档。
    doc_body.SaveToFile(doc_with_body_content_path, FileFormat.Docx)
    doc_body.Close()
    print(f"已创建并保存包含段落和表格的文档:{
     
     doc_with_body_content_path}")

    # 1.6 重新加载文档。
    loaded_doc_body = Document(doc_with_body_content_path)
    # 获取第一个节的Body对象。
    # 文档至少有一个节,通常我们操作的是第一个节的主体。
    body = loaded_doc_body.Sections[0].Body 
    print(f"\n--- 正在读取文档主体内容 ---")

    # 1.7 遍历Body中的所有段落。
    print("\n--- 段落内容 ---")
    # body.Paragraphs是一个集合,包含Body中所有的Paragraph对象。
    for i, para in enumerate(body.Paragraphs):
        # para.Text属性获取段落的纯文本内容。
        print(f"  段落 {
     
     i+1}: {
     
     para.Text.strip()}") # .strip()去除文本前后的空白符

    # 1.8 遍历Body中的所有表格。
    print("\n--- 表格内容 ---")
    # body.Tables是一个集合,包含Body中所有的Table对象。
    for i, tbl in enumerate(body.Tables):
        print(f"  表格 {
     
     i+1} (行数: {
     
     tbl.Rows.Count}, 列数: {
     
     tbl.Rows[0].Cells.Count if tbl.Rows.Count > 0 else 0}):")
        # 遍历表格的每一行。
        # tbl.Rows是一个TableRowCollection。
        for r_idx, row in enumerate(tbl.Rows):
            row_content = [] # 用于存储当前行所有单元格的文本
            # 遍历行中的每一个单元格。
            # row.Cells是一个TableCellCollection。
            for c_idx, cell in enumerate(row.Cells):
                # cell.Text属性获取单元格的纯文本内容。
                # 单元格内容通常也是由段落构成的,cell.Text会聚合所有段落文本。
                row_content.append(cell.Text.strip())
            # 使用制表符'\t'连接单元格内容,模拟表格输出。
            print(f"    行 {
     
     r_idx+1}: " + "\t".join(row_content))

    # 1.9 演示通过ChildObjects遍历所有顶级内容块。
    print("\n--- 通过ChildObjects遍历所有顶级内容块 ---")
    # body.ChildObjects是一个通用集合,包含Body下所有类型为IDocumentObject的对象。
    for i, doc_obj in enumerate(body.ChildObjects):
        # Spire.Doc对象都有DocumentObjectType属性,可以用来判断对象的具体类型。
        # DocumentObjectType.Paragraph表示当前对象是段落。
        if doc_obj.DocumentObjectType == DocumentObjectType.Paragraph:
            # 将通用对象强制转换为Paragraph类型。
            # 这允许我们访问Paragraph特有的属性,如Text。
            paragraph_obj = Paragraph(doc_obj.Owner, doc_obj.DocumentObjectType) # 重新包装为Paragraph对象
            # 注意:在Python中,Spire.Doc的C#底层对象到Python包装器的转换有时需要显式地通过构造函数或类型转换。
            # 简单起见,如果确定类型,直接访问属性通常也能工作,但更严谨的做法是确保类型匹配。
            # 对于读取,直接访问doc_obj.Text通常有效,因为它继承自DocumentObject。
            print(f"  第 {
     
     i+1} 个对象 (段落): {
     
     doc_obj.Text.strip()[:50]}...")
        # DocumentObjectType.Table表示当前对象是表格。
        elif doc_obj.DocumentObjectType == DocumentObjectType.Table:
            # 将通用对象强制转换为Table类型。
            table_obj = Table(doc_obj.Owner, doc_obj.DocumentObjectType) # 重新包装为Table对象
            # 对于表格,直接访问其行数和列数。
            print(f"  第 {
     
     i+1} 个对象 (表格): 行数={
     
     table_obj.Rows.Count}, 列数={
     
     table_obj.Rows[0].Cells.Count if table_obj.Rows.Count > 0 else 0}")
        else:
            # 打印其他未知类型对象的类型。
            print(f"  第 {
     
     i+1} 个对象 (未知类型): {
     
     doc_obj.DocumentObjectType}")

    # 1.10 关闭文档。
    loaded_doc_body.Close()
    print(f"已关闭文档:{
     
     doc_with_body_content_path}")

except Exception as e:
    print(f"发生错误:{
     
     e}")
finally:
    if os.path.exists(doc_with_body_content_path):
        os.remove(doc_with_body_content_path)
        print(f"已删除临时文档:{
     
     doc_with_body_content_path}")

中文解释:

  • from Spire.Doc import Document, from Spire.Doc.FileFormat import FileFormat, from Spire.Doc.Documents import Paragraph, Table, import os:导入所需的类和模块。ParagraphTable类用于类型判断和更具体的属性访问。
  • doc_with_body_content_path = os.path.join(os.getcwd(), "BodyContentDemo.docx"):定义一个用于演示的文档路径。
  • doc_body = Document():创建一个新文档。
  • section = doc_body.AddSection():添加一个节。
  • paragraph1 = section.AddParagraph():在节中添加第一个段落。
  • paragraph1.AppendText("这是文档主体中的第一个普通段落。"):向段落中添加文本。
  • table = section.AddTable():在节中添加一个表格。
  • table.ResetCells(3, 3):将表格初始化为3行3列。
  • table.Rows[0].Cells[0].AddParagraph().AppendText("表头1"):访问表格的第一行、第一个单元格,并向其中添加文本。重复此操作以填充表格。
  • doc_body.SaveToFile(doc_with_body_content_path, FileFormat.Docx):保存文档。
  • doc_body.Close():关闭文档。
  • loaded_doc_body = Document(doc_with_body_content_path):重新加载文档。
  • body = loaded_doc_body.Sections[0].Body:获取文档中第一个节的Body对象。通常,如果文档结构简单,第一个节的Body包含了大部分内容。
  • for i, para in enumerate(body.Paragraphs)::遍历Body对象中的Paragraphs集合,获取每一个Paragraph对象。
  • print(f" 段落 {i+1}: {para.Text.strip()}"):打印段落的序号和其纯文本内容。.strip()用于去除文本两端的空白字符。
  • for i, tbl in enumerate(body.Tables)::遍历Body对象中的Tables集合,获取每一个Table对象。
  • print(f" 表格 {i+1} (行数: {tbl.Rows.Count}, 列数: {tbl.Rows[0].Cells.Count if tbl.Rows.Count > 0 else 0}):"):打印表格的序号、行数和列数(注意对空表格的判断)。
  • for r_idx, row in enumerate(tbl.Rows)::遍历表格的每一行(TableRow对象)。
  • for c_idx, cell in enumerate(row.Cells)::遍历行中的每一个单元格(TableCell对象)。
  • row_content.append(cell.Text.strip()):将单元格的纯文本内容添加到列表中。cell.Text会递归获取单元格内所有段落的文本。
  • print(f" 行 {r_idx+1}: " + "\t".join(row_content)):打印当前行的所有单元格内容,用制表符分隔。
  • for i, doc_obj in enumerate(body.ChildObjects)::遍历Body对象中的ChildObjects集合。这是一个更通用的集合,包含了所有顶级子对象,无论其类型是段落、表格还是其他。
  • if doc_obj.DocumentObjectType == DocumentObjectType.Paragraph::检查当前doc_obj的类型是否是段落。DocumentObjectType是Spire.Doc提供的一个枚举,用于判断文档对象的具体类型。
  • paragraph_obj = Paragraph(doc_obj.Owner, doc_obj.DocumentObjectType):在某些情况下,为了调用特定类型的方法或属性,可能需要将通用DocumentObject强制转换为更具体的类型。这里是示例性地展示了如何进行类型转换,但通常可以直接访问doc_obj.Text
  • print(f" 第 {i+1} 个对象 (段落): {doc_obj.Text.strip()[:50]}..."):打印识别为段落的对象的内容。
  • elif doc_obj.DocumentObjectType == DocumentObjectType.Table::检查是否为表格类型。
  • table_obj = Table(doc_obj.Owner, doc_obj.DocumentObjectType):同样,进行类型转换。
  • print(f" 第 {i+1} 个对象 (表格): 行数={table_obj.Rows.Count}, 列数={table_obj.Rows[0].Cells.Count if table_obj.Rows.Count > 0 else 0}"):打印识别为表格的对象的信息。
  • else::处理其他未识别的文档对象类型。
  • print(f" 第 {i+1} 个对象 (未知类型): {doc_obj.DocumentObjectType}"):打印未知对象的类型。
  • finally:块用于os.remove(doc_with_body_content_path):删除临时文件。

这个示例全面展示了如何通过Body对象访问和区分文档中的主要内容块,为后续更精细的内容提取奠定了基础。


请注意,鉴于您对内容深度和字数的要求,以上只是一个极其微小的开端。我将持续以这种详细程度和原创性生成后续章节,覆盖从段落、文本范围到表格、图片、高级格式、内部机制、实战应用等所有方面。

我将继续生成,请稍候。### 第二章:Spire.Doc文档对象模型(DOM)的深层解析(续)

2.4 Paragraph对象:文本内容的基石与格式容器

Paragraph(段落)对象是Word文档中最基本的块级元素,也是承载文本内容的主要容器。在Spire.Doc中,一个Paragraph对象可以包含一个或多个TextRange对象,这些TextRange对象分别持有不同格式的文本。此外,段落还可以包含图片、文本框、书签、超链接等内联对象。

2.4.1 Paragraph对象的获取与遍历

如前所述,Paragraph对象通常从Body对象的Paragraphs集合中获取。

from Spire.Doc import Document
from Spire.Doc.FileFormat import FileFormat
from Spire.Doc.Documents import Paragraph, TextRange
import os

# 1. 准备一个包含不同格式段落的示例文档。
formatted_paragraph_doc_path = os.path.join(os.getcwd(), "FormattedParagraphs.docx")

try:
    # 1.1 创建新文档。
    doc_para = Document()
    section = doc_para.AddSection()

    # 1.2 添加一个普通段落。
    para1 = section.AddParagraph()
    para1.AppendText("这是一个普通段落,没有任何特殊格式。")

    # 1.3 添加一个包含粗体和斜体的段落。
    para2 = section.AddParagraph()
    # para2.AppendText()方法会返回一个TextRange对象,我们可以对其设置格式。
    text_range1 = para2.AppendText("这部分文本是普通文本,")
    text_range2 = para2.AppendText("这部分是粗体文本,")
    # 设置TextRange的IsBold属性为True,使其变为粗体。
    text_range2.CharacterFormat.IsBold = True
    text_range3 = para2.AppendText("而这部分是斜体文本。")
    # 设置TextRange的IsItalic属性为True,使其变为斜体。
    text_range3.CharacterFormat.IsItalic = True

    # 1.4 添加一个包含特定字体和颜色的段落。
    para3 = section.AddParagraph()
    text_range4 = para3.AppendText("这段文本使用红色和特定字体显示。")
    # 设置TextRange的TextColor属性为Spire.Doc.Color.Red。
    # Spire.Doc.Color是一个枚举或类,提供预定义的颜色。
    from Spire.Doc.Color import Color
    text_range4.CharacterFormat.TextColor = Color.Red
    # 设置TextRange的FontName属性为"Arial Black"。
    text_range4.CharacterFormat.FontName = "Arial Black"
    # 设置TextRange的FontSize属性为16磅。
    text_range4.CharacterFormat.FontSize = 16.0

    # 1.5 保存文档。
    doc_para.SaveToFile(formatted_paragraph_doc_path, FileFormat.Docx)
    doc_para.Close()
    print(f"已创建并保存包含不同格式段落的文档:{
     
     formatted_paragraph_doc_path}")

    # 1.6 重新加载文档。
    loaded_doc_para = Document(formatted_paragraph_doc_path)
    body = loaded_doc_para.Sections[0].Body
    print(f"\n--- 正在读取段落内容及其格式 ---")

    # 1.7 遍历Body中的所有段落。
    for i, para in enumerate(body.Paragraphs):
        print(f"\n--- 段落 {
     
     i+1} ---")
        # 直接获取段落的纯文本内容。
        print(f"  段落纯文本: {
     
     para.Text.strip()}")

        # 1.7.1 访问段落的字符格式(如果段落只有一个TextRange或所有TextRange格式相同)。
        # 注意:Paragraph的Format属性通常反映段落默认的字符格式或第一个TextRange的格式。
        # 如果段落内有多个TextRange且格式不同,需要遍历TextRanges。
        # 我们可以尝试获取第一个TextRange的格式作为段落的“主要”格式。
        if para.ChildObjects.Count > 0 and para.ChildObjects[0].DocumentObjectType == DocumentObjectType.TextRange:
            # 将ChildObjects[0]转换为TextRange对象。
            first_text_range = TextRange(para.ChildObjects[0].Owner, para.ChildObjects[0].DocumentObjectType)
            print(f"  (尝试)第一个文本范围字体: {
     
     first_text_range.CharacterFormat.FontName}")
            print(f"  (尝试)第一个文本范围字号: {
     
     first_text_range.CharacterFormat.FontSize}")
            # 由于Spire.Doc的Color对象是特定的,直接打印可能显示的是对象引用。
            # 我们可以尝试获取其RGB值或其他可识别的表示。
            # 通常需要将Color对象转换为其RGB值或十六进制字符串以便于识别。
            # Spire.Doc.Color对象有R, G, B属性。
            color = first_text_range.CharacterFormat.TextColor
            print(f"  (尝试)第一个文本范围颜色: R={
     
     color.R}, G={
     
     color.G}, B={
     
     color.B}")


        # 1.7.2 遍历段落中的所有子对象(TextRange、DocPicture、BookmarkStart等)。
        # Paragraph的ChildObjects集合包含了段落内部所有内联元素。
        print("  段落内部元素及格式:")
        # 从Spire.Doc.Documents命名空间导入DocumentObjectType枚举。
        from Spire.Doc.Documents import DocumentObjectType
        for j, child_obj in enumerate(para.ChildObjects):
            # 判断子对象的类型。
            if child_obj.DocumentObjectType == DocumentObjectType.TextRange:
                # 将通用DocumentObject强制转换为TextRange类型。
                text_range_obj = TextRange(child_obj.Owner, child_obj.DocumentObjectType)
                # 获取文本范围的文本。
                text = text_range_obj.Text
                # 获取字符格式。
                char_format = text_range_obj.CharacterFormat
                # 检查并打印格式属性。
                formats = []
                if char_format.IsBold:
                    formats.append("粗体")
                if char_format.IsItalic:
                    formats.append("斜体")
                if char_format.IsUnderline:
                    formats.append("下划线")
                # 检查字体名称和大小。
                if char_format.FontName:
                    formats.append(f"字体: {
     
     char_format.FontName}")
                if char_format.FontSize > 0:
                    formats.append(f"字号: {
     
     char_format.FontSize}")
                # 检查颜色。
                if char_format.TextColor != Color.Empty: # Color.Empty表示未设置或默认颜色
                    color_val = char_format.TextColor
                    formats.append(f"颜色: R={
     
     color_val.R},G={
     
     color_val.G},B={
     
     color_val.B}")

                print(f"    - 元素 {
     
     j+1} (TextRange): '{
     
     text}' ({
     
     ', '.join(formats) if formats else '无特殊格式'})")
            elif child_obj.DocumentObjectType == DocumentObjectType.BookmarkStart:
                # 获取书签开始标记的名称。
                bookmark_name = child_obj.Name
                print(f"    - 元素 {
     
     j+1} (BookmarkStart): '{
     
     bookmark_name}'")
            elif child_obj.DocumentObjectType == DocumentObjectType.BookmarkEnd:
                bookmark_name = child_obj.Name
                print(f"    - 元素 {
     
     j+1} (BookmarkEnd): '{
     
     bookmark_name}'")
            elif child_obj.DocumentObjectType == DocumentObjectType.Break:
                # Break类型可以是换行符(Line Break)或分页符(Page Break)等。
                # Text属性通常可以获取换行符的表示。
                print(f"    - 元素 {
     
     j+1} (Break): {
     
     child_obj.Text.replace('\r', '\\r').replace('\n', '\\n')}") # 打印可见的换行符
            elif child_obj.DocumentObjectType == DocumentObjectType.Field:
                #
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宅男很神经

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值