Python操作Word书签:Free Spire.Doc替换与插图
Word 书签可以理解为文档里的命名变量:先在模板中标记位置,程序运行时按名称写入文本、表格或图片。使用 Free Spire.Doc for Python 可以把“模板 + 数据 = 文档”的流程自动化,定位比全文查找替换更精确,也支持嵌套书签。先安装:pip install spire.doc.free
一、创建书签模板
书签由 AppendBookmarkStart 和 AppendBookmarkEnd 成对定义,名称必须一致,End 位于 Start 之后。下面创建报告模板,并在待填充位置插入书签:
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)
# Summary、MilestoneTable、RiskLevel、RiskDetail、Signature 同理添加
document.SaveToFile('template.docx', FileFormat.Docx)
document.Close()
风险与建议部分可用嵌套书签:外层 RiskSection 包裹内层 RiskLevel 和 RiskDetail。内层可单独替换,外层也可整体操作。生成模板后包含 10 个书签:ProjectName、ClientName、ReportDate、ProjectPhase、Summary、MilestoneTable、RiskSection、RiskLevel、RiskDetail、Signature。
二、替换书签内容
BookmarksNavigator 是书签操作核心类。MoveToBookmark 按名称定位,ReplaceBookmarkContent 替换书签覆盖的内容。第二个参数控制是否保留原有字符格式:False 使用默认格式,True 继承书签内原文本的字体、字号、颜色等属性。
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...', False)
navigator.MoveToBookmark('RiskLevel')
navigator.ReplaceBookmarkContent('Medium', False)
doc.SaveToFile('step2_text.docx', FileFormat.Docx)
doc.Close()
示例摘要对应的事实数据包括:新增 320 个传感器接入点,日吞吐从 1.2 亿提升到 3.5 亿条记录,集成交通、环境、安全、水务、市政 5 个子系统,API 平均响应 87ms,可用性 99.6%,中期验收 14 项通过 13 项,剩余实时告警延迟预计 10 月 15 日达标。风险详情可写 Kafka 集群高峰消息堆积,峰值延迟 4.2 秒,超过 2 秒 SLA,扩容到 6 节点预计 10 月 10 日完成。嵌套书签内层可独立定位替换,MoveToBookmark 不关心嵌套层级。
替换为表格时,先构建 Table,再用 TextBodyPart 包装后传给 ReplaceBookmarkContent:
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.Cells
para = cell.AddParagraph()
txtRange = para.AppendText(data)
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) 的第二个参数启用固定布局模式,列宽由 ResetCells 自动分配。TextBodyPart 用于承载表格、多段落等结构化内容。
三、在书签位置插入图片
AppendPicture 必须在段落上调用,而书签位置不一定有现成空段落。可先在临时 section 中创建带图片的段落,再用 InsertParagraph 插入书签位置,最后移除临时 section:
doc = Document()
doc.LoadFromFile('step3_table.docx')
bn = BookmarksNavigator(doc)
bn.MoveToBookmark('Signature', True, True)
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)
doc.Sections.Remove(section0)
doc.SaveToFile('step4_image.docx', FileFormat.Docx)
doc.Close()
MoveToBookmark 的后两个 True 分别控制是否区分大小写、是否向前搜索。Width 和 Height 单位为磅,150 磅约等于 5.3 厘米,适合签章图片;横幅图片可按比例设置,如 Width=400.0、Height=100.0。
四、提取与列举书签内容
document.Bookmarks 是书签集合,支持按索引和按名称访问:
doc = Document()
doc.LoadFromFile('step4_image.docx')
for i in range(doc.Bookmarks.Count):
bm = doc.Bookmarks
print(f'{i+1}. {bm.Name}')
bookmark = doc.Bookmarks['ProjectName']
print(bookmark.Name)
GetBookmarkContent 返回书签内容对象,遍历 BodyItems 可取出文本。若书签包含表格,还需用 isinstance 判断 Table 类型:
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}')
五、删除书签与书签内容
只删除书签标记、保留内容,可用 Bookmarks.Remove:
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 个书签,适合报告定稿时清理标记。若要同时删除书签及其内容,需要定位 BookmarkStart 和 BookmarkEnd 在段落子对象中的索引,再循环移除两者之间的元素:
bookmark = doc.Bookmarks['Signature']
para = bookmark.BookmarkStart.Owner
startIndex = para.ChildObjects.IndexOf(bookmark.BookmarkStart)
para2 = bookmark.BookmarkEnd.Owner
endIndex = para2.ChildObjects.IndexOf(bookmark.BookmarkEnd)
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()
RemoveAt 后列表会前移,因此循环中始终删除 startIndex + 1 位置,而不是递增索引。删除后同样剩余 9 个书签。
六、关键类与方法
常用类包括 Document、Section、Paragraph、TextRange、Table、TextBodyPart、BookmarksNavigator、Bookmarks。常用方法包括 AppendBookmarkStart/AppendBookmarkEnd、MoveToBookmark、ReplaceBookmarkContent、GetBookmarkContent、InsertParagraph、Bookmarks.Remove。
适用场景主要是项目报告、月报、合同、通知等模板化文档。典型流程是:创建带书签模板、替换文本书签、替换表格书签、在书签位置插入图片、保存输出;定稿阶段再按需删除书签标记。嵌套书签适合一个区域内多个字段独立填充,格式保留参数适合占位文本已预设样式的模板。需要注意:Start 与 End 必须成对且同名;MoveToBookmark 按名称查找;删除元素时注意集合索引变化;图片尺寸单位为磅。
Re: Python操作Word书签:Free Spire.Doc替换与插图
感谢分享,步骤写得很清楚。模板阶段用 AppendBookmarkStart 和 AppendBookmarkEnd 成对定义书签、名称保持一致,以及外层 RiskSection 包内层 RiskLevel、RiskDetail 的嵌套做法,很适合做报告类文档。替换部分里 BookmarksNavigator 的 MoveToBookmark 加 ReplaceBookmarkContent 很直观,第二个参数控制是否保留原格式这点对模板排版很重要。表格先建 Table 再用 TextBodyPart 包装传入,也让人知道书签里不只是能填文本。 不过帖子最后 Milestone 表格好像没贴完,标题提到的插图部分也还没看到。如果方便的话,想继续看图片插入书签以及嵌套书签整体替换的完整示例。Re: Python操作Word书签:Free Spire.Doc替换与插图
书签这套思路确实比全文查找替换稳得多,模板改版之后不用重写定位逻辑,这点很关键。 有几个地方想请教一下: 一是 ReplaceBookmarkContent 执行完之后,书签本身还在不在?如果还在,那同一份 document 上依次替换其余书签应该不会有冲突;但要是书签被吃掉,后面再有引用同一位置的操作就得改顺序了。之前用别的库踩过这个坑,替换完定位就失效。 二是嵌套书签那段,内层能独立定位这点很有用。不过如果先把外层 RiskSection 整体替换了,里层的 RiskLevel 和 RiskDetail 还能再进去改吗,还是必须从内到外按顺序来?感觉这个顺序问题很容易在生产里出岔子。 三是建议模板本身也脚本生成,别让人在 Word 里手工调。手工编辑最容易动的就是书签名字,改一个字符或者误删一个,运行时直接报找不到,排查起来还绕。 另外免费版好像对段落数或者表格数有上限,你这个 10 个书签的模板肯定没问题,但要是批量生成几十上百页的报告,最好先压测一下,确认不会被截断。 最后图片插入那段正文好像断了,方便的话补一下用书签占位再塞图的做法,正好跟表格替换那部分对应起来,这块应该是很多人最想看的。Re: Python操作Word书签:Free Spire.Doc替换与插图
感谢分享,这套用书签做模板填充的思路很清晰。用 AppendBookmarkStart 和 AppendBookmarkEnd 在模板里埋点,再通过 BookmarksNavigator 的 MoveToBookmark 和 ReplaceBookmarkContent 填充,确实比全文查找替换更稳,尤其适合报告类文档反复生成。 嵌套书签 RiskSection 包 RiskLevel 和 RiskDetail 这点很实用,内层能单独替换,外层也能整体处理,灵活性挺高。替换文本时第二个参数控制是否保留原格式,这个细节讲得也明白,实际做模板时很容易忽略。表格替换先用 Table 构建、再用 TextBodyPart 包装后传给 ReplaceBookmarkContent,这个步骤也很关键。 看正文里的 MilestoneTable 数据似乎还没贴完,期待后续把完整表格示例和插图部分补上,特别是书签位置插入图片的写法。整体很实用,先收藏学习了。
页:
[1]