Python使用Free Spire.Doc for Python操作Word书签的完整教学
Word 书签是文档中一段被命名标记的区域,作用类似编程中的变量——定义一次,按名引用。在报告模板中预先插入书签,程序运行时将书签内容替换为实际数据,就能把"模板 + 数据 = 文档"的流程完全自动化。相比手动查找替换,书签定位精确、可嵌套、可复用,且不依赖文档中的唯一文本片段。
本文使用 Free Spire.Doc for Python 演示 Word 书签的完整操作链:创建书签、替换文本与表格内容、在书签位置插入图片、提取书签内容、删除书签,以及将这些操作组合为项目报告的自动生成流程。
pip install spire.doc.free
1. 创建书签:在文档中标记内容位置
书签由一对标记定义:AppendBookmarkStart("name") 和 AppendBookmarkEnd("name"),两者之间的内容即为书签覆盖范围。下面创建一个项目报告模板,在待填充位置插入 10 个书签:
from spire.doc import *
from spire.doc.common import *
document = Document()
section = document.AddSection()
# 标题
p = section.AddParagraph()
p.AppendText("Project Phase Report")
p.ApplyStyle(BuiltinStyle.Heading1)
p.Format.HorizontalAlignment = HorizontalAlignment.Center
# 基本信息区——用书签标记待填充位置
p = section.AddParagraph()
p.AppendText("Project Name: ")
p.AppendBookmarkStart("ProjectName")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("ProjectName")
p = section.AddParagraph()
p.AppendText("Client: ")
p.AppendBookmarkStart("ClientName")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("ClientName")
p = section.AddParagraph()
p.AppendText("Report Date: ")
p.AppendBookmarkStart("ReportDate")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("ReportDate")
p = section.AddParagraph()
p.AppendText("Project Phase: ")
p.AppendBookmarkStart("ProjectPhase")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("ProjectPhase")
section.AddParagraph()
# 项目摘要
p = section.AddParagraph()
p.AppendText("Project Summary")
p.ApplyStyle(BuiltinStyle.Heading2)
p = section.AddParagraph()
p.AppendBookmarkStart("Summary")
p.AppendText("(Auto-filled by program)")
p.AppendBookmarkEnd("Summary")
section.AddParagraph()
# 里程碑进度表占位
p = section.AddParagraph()
p.AppendText("Milestone Status")
p.ApplyStyle(BuiltinStyle.Heading2)
p = section.AddParagraph()
p.AppendBookmarkStart("MilestoneTable")
p.AppendText("(Auto-inserted by program)")
p.AppendBookmarkEnd("MilestoneTable")
section.AddParagraph()
# 风险与建议——嵌套书签
p = section.AddParagraph()
p.AppendText("Risk and Suggestions")
p.ApplyStyle(BuiltinStyle.Heading2)
p = section.AddParagraph()
p.AppendBookmarkStart("RiskSection")
p.AppendBookmarkStart("RiskLevel")
p.AppendText("(risk level to be filled)")
p.AppendBookmarkEnd("RiskLevel")
p.AppendText(". ")
p.AppendBookmarkStart("RiskDetail")
p.AppendText("(risk detail to be filled)")
p.AppendBookmarkEnd("RiskDetail")
p.AppendBookmarkEnd("RiskSection")
section.AddParagraph()
# 签章区
p = section.AddParagraph()
p.AppendText("Signature")
p.ApplyStyle(BuiltinStyle.Heading2)
p = section.AddParagraph()
p.AppendBookmarkStart("Signature")
p.AppendText("(Auto-inserted by program)")
p.AppendBookmarkEnd("Signature")
document.SaveToFile("template.docx", FileFormat.Docx)
document.Close()风险与建议一节用了嵌套书签:外层 RiskSection 包裹 RiskLevel 和 RiskDetail 两个内层书签,三者同处一个段落。嵌套书签的好处是可以单独替换内层内容,也可以整体操作外层内容。AppendBookmarkStart 和 AppendBookmarkEnd 必须成对出现,名称一致,且 End 必须在 Start 之后。
运行后模板包含 10 个书签:
Template created: template.docx
Bookmark count: 10
Bookmarks: ['ProjectName', 'ClientName', 'ReportDate', 'ProjectPhase',
'Summary', 'MilestoneTable', 'RiskSection', 'RiskLevel',
'RiskDetail', 'Signature']
2. 替换书签内容:文本与表格
替换文本内容
BookmarksNavigator 是书签操作的核心类。通过 MoveToBookmark 定位到指定书签后,ReplaceBookmarkContent 将书签覆盖的内容替换为新文本:
from spire.doc import *
from spire.doc.common import *
doc = Document()
doc.LoadFromFile("template.docx")
navigator = BookmarksNavigator(doc)
navigator.MoveToBookmark("ProjectName")
navigator.ReplaceBookmarkContent("Smart City Data Platform Phase II", False)
navigator.MoveToBookmark("ClientName")
navigator.ReplaceBookmarkContent("Orient Municipal Group", False)
navigator.MoveToBookmark("ReportDate")
navigator.ReplaceBookmarkContent("September 28, 2026", False)
navigator.MoveToBookmark("ProjectPhase")
navigator.ReplaceBookmarkContent("Phase II - Mid-term Acceptance", False)
navigator.MoveToBookmark("Summary")
navigator.ReplaceBookmarkContent(
"This phase completed a comprehensive upgrade of the data collection module, "
"adding 320 sensor access points and increasing daily data throughput from "
"120 million to 350 million records. The platform has integrated with 5 subsystems "
"(traffic, environmental, security, water, and municipal), with an average API "
"response time of 87ms and 99.6% availability. Mid-term acceptance passed 13 of "
"14 indicators; the remaining 1 (real-time alert latency) is expected to meet "
"the target by October 15.", False)
# 嵌套书签的内层可以单独替换
navigator.MoveToBookmark("RiskLevel")
navigator.ReplaceBookmarkContent("Medium", False)
navigator.MoveToBookmark("RiskDetail")
navigator.ReplaceBookmarkContent(
"The Kafka cluster on which the real-time alert module depends experienced "
"message accumulation during peak hours, with a peak latency of 4.2 seconds, "
"exceeding the 2-second SLA limit. The ops team has been coordinated to expand "
"to 6 nodes, expected to complete by October 10.", False)
doc.SaveToFile("step2_text.docx", FileFormat.Docx)
doc.Close()ReplaceBookmarkContent 的第二个参数是布尔值,控制是否保留书签原有的字符格式。传 False 时新文本使用默认格式,传 True 时继承书签内原有文本的字体、字号、颜色等属性。模板中书签内容是 "(to be filled)" 占位文本,如果模板设计者把这些占位文本设为特定格式(比如蓝色宋体),传 True 可以让替换后的数据保持同样的外观。
嵌套书签的内层 RiskLevel 和 RiskDetail 可以独立定位和替换,与外层 RiskSection 互不干扰。MoveToBookmark 按名称查找,不关心书签的嵌套层级。
替换为表格
书签内容不限于文本,也可以替换为表格。先构建 Table 对象,再通过 TextBodyPart 包装后替换:
doc = Document()
doc.LoadFromFile("step2_text.docx")
table = Table(doc, True)
table.ResetCells(6, 4)
data = [
["Milestone", "Planned Date", "Actual Date", "Status"],
["Requirements", "2026-07-15", "2026-07-14", "Completed"],
["Architecture", "2026-07-30", "2026-07-30", "Completed"],
["Core Development", "2026-08-31", "2026-09-02", "Completed (2-day delay)"],
["Integration Test", "2026-09-20", "—", "In Progress (85%)"],
["Mid-term Review", "2026-09-28", "2026-09-28", "Passed"],
]
for i in range(6):
for j in range(4):
cell = table.Rows[i].Cells[j]
para = cell.AddParagraph()
txtRange = para.AppendText(data[i][j])
if i == 0:
txtRange.CharacterFormat.Bold = True
navigator = BookmarksNavigator(doc)
navigator.MoveToBookmark("MilestoneTable")
part = TextBodyPart(doc)
part.BodyItems.Add(table)
navigator.ReplaceBookmarkContent(part)
doc.SaveToFile("step3_table.docx", FileFormat.Docx)
doc.Close()Table(doc, True) 的第二个参数 True 启用表格的固定布局模式,列宽由 ResetCells 自动分配。TextBodyPart 是承载表格的容器——ReplaceBookmarkContent 接受字符串和 TextBodyPart 两种参数类型,后者用于替换结构化内容(表格、多段落等)。

3. 在书签位置插入图片
向书签位置插入图片需要借助 InsertParagraph 方法。由于 AppendPicture 必须在段落上调用,而书签位置不一定有现成的空段落,操作方式是先在一个临时 section 中创建带图片的段落,再将该段落插入到书签位置:
doc = Document()
doc.LoadFromFile("step3_table.docx")
bn = BookmarksNavigator(doc)
bn.MoveToBookmark("Signature", True, True)
# 创建临时 section 放图片段落
section0 = doc.AddSection()
paragraph = section0.AddParagraph()
paragraph.Format.HorizontalAlignment = HorizontalAlignment.Center
picture = paragraph.AppendPicture("signature.png")
picture.Width = 150.0
picture.Height = 150.0
# 将图片段落插入书签位置
bn.InsertParagraph(paragraph)
# 移除临时 section
doc.Sections.Remove(section0)
doc.SaveToFile("step4_image.docx", FileFormat.Docx)
doc.Close()MoveToBookmark("Signature", True, True) 的后两个参数控制查找行为:第一个 True 表示是否区分大小写,第二个 True 表示是否向前搜索。InsertParagraph 将段落插入到书签当前位置之后,图片随之落入文档。临时 section 在插入完成后移除,不产生多余页面。
picture.Width 和 picture.Height 的单位是磅(point),150 磅约等于 5.3 厘米,适合圆形签章的尺寸。如果插入的是横幅图片,可以按比例设置,比如 Width = 400.0, Height = 100.0。

4. 提取与列举书签内容
处理他人提供的文档时,经常需要先了解文档中有哪些书签、各书签包含什么内容。
列举所有书签
document.Bookmarks 是书签集合,支持按索引和按名称两种方式访问:
doc = Document()
doc.LoadFromFile("step4_image.docx")
for i in range(doc.Bookmarks.Count):
bm = doc.Bookmarks[i]
print(f" {i+1}. {bm.Name}")
print(f"Total: {doc.Bookmarks.Count} bookmarks")
# 按名称直接访问
bookmark = doc.Bookmarks["ProjectName"]
print(f"ProjectName bookmark found, name: {bookmark.Name}")输出:
1. ProjectName
2. ClientName
3. ReportDate
4. ProjectPhase
5. Summary
6. MilestoneTable
7. RiskSection
8. RiskLevel
9. RiskDetail
10. Signature
Total: 10 bookmarks
提取书签文本
BookmarksNavigator.GetBookmarkContent() 返回书签覆盖的内容对象,遍历其 BodyItems 可以取出文本:
navigator = BookmarksNavigator(doc)
targets = ["ProjectName", "ClientName", "Summary", "RiskLevel", "RiskDetail"]
for name in targets:
navigator.MoveToBookmark(name)
content = navigator.GetBookmarkContent()
text = ""
for i in range(content.BodyItems.Count):
item = content.BodyItems.get_Item(i)
if isinstance(item, Paragraph):
for j in range(item.ChildObjects.Count):
child = item.ChildObjects.get_Item(j)
if isinstance(child, TextRange):
text += child.Text
display = text[:80] + "..." if len(text) > 80 else text
print(f" [{name}] {display}")输出:
[ProjectName] Smart City Data Platform Phase II
[ClientName] Orient Municipal Group
[Summary] This phase completed a comprehensive upgrade of the data collection module, addi...
[RiskLevel] Medium
[RiskDetail] The Kafka cluster on which the real-time alert module depends experienced messag...
GetBookmarkContent 返回的 TextBodyPart 中可能包含段落、表格等多种元素,遍历时需要用 isinstance 判断类型。对于纯文本书签,只处理 Paragraph 和 TextRange 即可;如果书签覆盖了表格,还需要处理 Table 类型。
5. 删除书签与书签内容
删除操作有两种:只删书签标记(保留内容)和同时删除标记与内容。
删除书签标记,保留内容
document.Bookmarks.Remove(bookmark) 只移除书签的 Start 和 End 标记,书签内的文本或表格原样保留在文档中:
doc = Document()
doc.LoadFromFile("step4_image.docx")
bookmark = doc.Bookmarks["RiskSection"]
doc.Bookmarks.Remove(bookmark)
doc.SaveToFile("step6a_remove_mark.docx", FileFormat.Docx)
doc.Close()
# 删除后剩余 9 个书签(原 10 个减去 RiskSection)这种操作适用于报告定稿阶段:填充完成后移除所有书签标记,输出干净的最终文档,同时保留填充的内容。
删除书签及其内容
要同时删除书签标记和内容,需要手动定位 BookmarkStart 和 BookmarkEnd 在段落中的位置索引,然后逐个移除两者之间的子对象:
doc = Document()
doc.LoadFromFile("step4_image.docx")
bookmark = doc.Bookmarks["Signature"]
para = bookmark.BookmarkStart.Owner
startIndex = para.ChildObjects.IndexOf(bookmark.BookmarkStart)
para2 = bookmark.BookmarkEnd.Owner
endIndex = para2.ChildObjects.IndexOf(bookmark.BookmarkEnd)
# 移除 Start 和 End 之间的内容
for i in range(startIndex + 1, endIndex):
para.ChildObjects.RemoveAt(startIndex + 1)
# 再移除书签标记本身
doc.Bookmarks.Remove(bookmark)
doc.SaveToFile("step6b_remove_content.docx", FileFormat.Docx)
doc.Close()
# 删除后剩余 9 个书签BookmarkStart.Owner 返回书签标记所在的段落对象,ChildObjects.IndexOf 获取标记在段落子对象列表中的位置。循环 RemoveAt(startIndex + 1) 每次删除 Start 后面的第一个元素,直到删到 End 前一个位置为止。注意 RemoveAt 后列表会移位,所以循环中始终删除 startIndex + 1 位置而非递增索引。
6. 综合应用:项目报告自动生成
将上述操作组合起来,一个完整的报告生成流程是:创建带书签的模板 → 替换文本书签 → 替换表格书签 → 插入签章图片 → 保存输出。以下脚本一次运行完成全部步骤:
from spire.doc import *
from spire.doc.common import *
document = Document()
section = document.AddSection()
# --- 创建模板 ---
p = section.AddParagraph()
p.AppendText("Project Phase Report")
p.ApplyStyle(BuiltinStyle.Heading1)
p.Format.HorizontalAlignment = HorizontalAlignment.Center
fields = [
("Project Name: ", "ProjectName"),
("Client: ", "ClientName"),
("Report Date: ", "ReportDate"),
("Project Phase: ", "ProjectPhase"),
]
for label, bm in fields:
p = section.AddParagraph()
p.AppendText(label)
p.AppendBookmarkStart(bm)
p.AppendText("(to be filled)")
p.AppendBookmarkEnd(bm)
section.AddParagraph()
p = section.AddParagraph()
p.AppendText("Project Summary")
p.ApplyStyle(BuiltinStyle.Heading2)
p = section.AddParagraph()
p.AppendBookmarkStart("Summary")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("Summary")
section.AddParagraph()
p = section.AddParagraph()
p.AppendText("Milestone Status")
p.ApplyStyle(BuiltinStyle.Heading2)
p = section.AddParagraph()
p.AppendBookmarkStart("MilestoneTable")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("MilestoneTable")
section.AddParagraph()
p = section.AddParagraph()
p.AppendText("Risk and Suggestions")
p.ApplyStyle(BuiltinStyle.Heading2)
p = section.AddParagraph()
p.AppendBookmarkStart("RiskLevel")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("RiskLevel")
p.AppendText(". ")
p.AppendBookmarkStart("RiskDetail")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("RiskDetail")
section.AddParagraph()
p = section.AddParagraph()
p.AppendText("Signature")
p.ApplyStyle(BuiltinStyle.Heading2)
p = section.AddParagraph()
p.AppendBookmarkStart("Signature")
p.AppendText("(to be filled)")
p.AppendBookmarkEnd("Signature")
# --- 填充文本 ---
navigator = BookmarksNavigator(document)
text_data = {
"ProjectName": "Smart City Data Platform Phase II",
"ClientName": "Orient Municipal Group",
"ReportDate": "September 28, 2026",
"ProjectPhase": "Phase II - Mid-term Acceptance",
"Summary": "This phase completed a comprehensive upgrade of the data collection module, "
"adding 320 sensor access points and increasing daily data throughput from "
"120 million to 350 million records. The platform has integrated with 5 subsystems "
"(traffic, environmental, security, water, and municipal), with an average API "
"response time of 87ms and 99.6% availability. Mid-term acceptance passed 13 of "
"14 indicators; the remaining 1 (real-time alert latency) is expected to meet "
"the target by October 15.",
"RiskLevel": "Medium",
"RiskDetail": "The Kafka cluster on which the real-time alert module depends experienced "
"message accumulation during peak hours, with a peak latency of 4.2 seconds, "
"exceeding the 2-second SLA limit. The ops team has been coordinated to expand "
"to 6 nodes, expected to complete by October 10.",
}
for name, value in text_data.items():
navigator.MoveToBookmark(name)
navigator.ReplaceBookmarkContent(value, False)
# --- 插入里程碑表格 ---
table = Table(document, True)
table.ResetCells(6, 4)
data = [
["Milestone", "Planned Date", "Actual Date", "Status"],
["Requirements", "2026-07-15", "2026-07-14", "Completed"],
["Architecture", "2026-07-30", "2026-07-30", "Completed"],
["Core Development", "2026-08-31", "2026-09-02", "Completed (2-day delay)"],
["Integration Test", "2026-09-20", "—", "In Progress (85%)"],
["Mid-term Review", "2026-09-28", "2026-09-28", "Passed"],
]
for i in range(6):
for j in range(4):
cell = table.Rows[i].Cells[j]
para = cell.AddParagraph()
txtRange = para.AppendText(data[i][j])
if i == 0:
txtRange.CharacterFormat.Bold = True
navigator.MoveToBookmark("MilestoneTable")
part = TextBodyPart(document)
part.BodyItems.Add(table)
navigator.ReplaceBookmarkContent(part)
# --- 插入签章图片 ---
bn = BookmarksNavigator(document)
bn.MoveToBookmark("Signature", True, True)
section0 = document.AddSection()
paragraph = section0.AddParagraph()
paragraph.Format.HorizontalAlignment = HorizontalAlignment.Center
picture = paragraph.AppendPicture("signature.png")
picture.Width = 150.0
picture.Height = 150.0
bn.InsertParagraph(paragraph)
document.Sections.Remove(section0)
# --- 保存 ---
document.SaveToFile("final_report.docx", FileFormat.Docx)
document.Close()运行结果:
Final report generated: final_report.docx
Bookmark count: 9
Bookmarks: ['ProjectName', 'ClientName', 'ReportDate', 'ProjectPhase',
'Summary', 'MilestoneTable', 'RiskLevel', 'RiskDetail', 'Signature']
最终报告包含 9 个书签(综合模板未创建外层 RiskSection,只保留了内层的 RiskLevel 和 RiskDetail)。书签在替换后仍然存在于文档中,可以用于后续的二次填充或内容提取。如果需要输出不含书签的定稿,在保存前遍历 document.Bookmarks 逐个 Remove 即可。

7. 关键类与方法
| 类 / 方法 | 说明 |
|---|---|
Paragraph.AppendBookmarkStart(name) | 在段落中添加书签起始标记 |
Paragraph.AppendBookmarkEnd(name) | 在段落中添加书签结束标记 |
Document.Bookmarks | 书签集合,支持按索引 [0] 和按名称 ["name"] 访问 |
Document.Bookmarks.Remove(bookmark) | 删除书签标记,保留内容 |
BookmarksNavigator(doc) | 书签导航器,用于定位和操作书签内容 |
BookmarksNavigator.MoveToBookmark(name) | 定位到指定书签 |
BookmarksNavigator.GetBookmarkContent() | 获取书签覆盖的内容对象 |
BookmarksNavigator.ReplaceBookmarkContent(text, isKeepFormat) | 替换书签内容为文本 |
BookmarksNavigator.ReplaceBookmarkContent(textBodyPart) | 替换书签内容为结构化内容(表格等) |
BookmarksNavigator.InsertParagraph(paragraph) | 在书签位置插入段落 |
TextBodyPart(doc) | 内容容器,用于包装表格等结构化内容 |
8. 适用场景
书签方案适合"模板固定、数据变化"的文档生成任务。模板由业务人员设计并插入书签,程序负责填充——两者解耦,修改模板格式不影响填充逻辑。
典型场景包括:项目报告定期生成(周报、月报、阶段验收报告)、合同模板填充(甲乙方信息、金额、期限)、产品说明书组装(按配置选取章节插入主文档)、批量通知函生成(收件人、事项、金额各异)。当填充来源是数据库或 Excel 时,将查询结果组织为字典,循环调用 ReplaceBookmarkContent 即可批量产出。
与邮件合并相比,书签方案的优势在于可以插入表格、图片等结构化内容,且不要求模板中预先存在合并域。劣势是批量生成时每次需要重新加载模板文件(ReplaceBookmarkContent 会消耗书签),而邮件合并的 Execute 方法可以一次性填充多个域。两者可以根据具体需求选用。
以上就是Python使用Free Spire.Doc for Python操作Word书签的完整教学的详细内容,更多关于Python操作Word书签的资料请关注脚本之家其它相关文章!
相关文章
详解Django自定义图片和文件上传路径(upload_to)的2种方式
这篇文章主要介绍了详解Django自定义图片和文件上传路径(upload_to)的2种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2020-12-12


最新评论