查看: 10673|回复: 3

Python操作Word书签:Free Spire.Doc替换与插图

[复制链接]
发表于 2026-10-3 12:00:00 | 显示全部楼层 |阅读模式
Word 书签可以理解为文档里的命名变量:先在模板中标记位置,程序运行时按名称写入文本、表格或图片。使用 Free Spire.Doc for Python 可以把“模板 + 数据 = 文档”的流程自动化,定位比全文查找替换更精确,也支持嵌套书签。先安装:
  1. pip install spire.doc.free
复制代码

一、创建书签模板

书签由 AppendBookmarkStart 和 AppendBookmarkEnd 成对定义,名称必须一致,End 位于 Start 之后。下面创建报告模板,并在待填充位置插入书签:
  1. from spire.doc import *
  2. from spire.doc.common import *
  3. document = Document()
  4. section = document.AddSection()
  5. p = section.AddParagraph()
  6. p.AppendText('Project Phase Report')
  7. p.ApplyStyle(BuiltinStyle.Heading1)
  8. p.Format.HorizontalAlignment = HorizontalAlignment.Center
  9. fields = [
  10.     ('Project Name: ', 'ProjectName'),
  11.     ('Client: ', 'ClientName'),
  12.     ('Report Date: ', 'ReportDate'),
  13.     ('Project Phase: ', 'ProjectPhase'),
  14. ]
  15. for label, bm in fields:
  16.     p = section.AddParagraph()
  17.     p.AppendText(label)
  18.     p.AppendBookmarkStart(bm)
  19.     p.AppendText('(to be filled)')
  20.     p.AppendBookmarkEnd(bm)
  21. # Summary、MilestoneTable、RiskLevel、RiskDetail、Signature 同理添加
  22. document.SaveToFile('template.docx', FileFormat.Docx)
  23. document.Close()
复制代码

风险与建议部分可用嵌套书签:外层 RiskSection 包裹内层 RiskLevel 和 RiskDetail。内层可单独替换,外层也可整体操作。生成模板后包含 10 个书签:ProjectName、ClientName、ReportDate、ProjectPhase、Summary、MilestoneTable、RiskSection、RiskLevel、RiskDetail、Signature。

二、替换书签内容

BookmarksNavigator 是书签操作核心类。MoveToBookmark 按名称定位,ReplaceBookmarkContent 替换书签覆盖的内容。第二个参数控制是否保留原有字符格式:False 使用默认格式,True 继承书签内原文本的字体、字号、颜色等属性。
  1. doc = Document()
  2. doc.LoadFromFile('template.docx')
  3. navigator = BookmarksNavigator(doc)
  4. navigator.MoveToBookmark('ProjectName')
  5. navigator.ReplaceBookmarkContent('Smart City Data Platform Phase II', False)
  6. navigator.MoveToBookmark('ClientName')
  7. navigator.ReplaceBookmarkContent('Orient Municipal Group', False)
  8. navigator.MoveToBookmark('ReportDate')
  9. navigator.ReplaceBookmarkContent('September 28, 2026', False)
  10. navigator.MoveToBookmark('ProjectPhase')
  11. navigator.ReplaceBookmarkContent('Phase II - Mid-term Acceptance', False)
  12. navigator.MoveToBookmark('Summary')
  13. navigator.ReplaceBookmarkContent(
  14.     'This phase completed a comprehensive upgrade of the data collection module...', False)
  15. navigator.MoveToBookmark('RiskLevel')
  16. navigator.ReplaceBookmarkContent('Medium', False)
  17. doc.SaveToFile('step2_text.docx', FileFormat.Docx)
  18. 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:
  1. table = Table(doc, True)
  2. table.ResetCells(6, 4)
  3. data = [
  4.     ['Milestone', 'Planned Date', 'Actual Date', 'Status'],
  5.     ['Requirements', '2026-07-15', '2026-07-14', 'Completed'],
  6.     ['Architecture', '2026-07-30', '2026-07-30', 'Completed'],
  7.     ['Core Development', '2026-08-31', '2026-09-02', 'Completed (2-day delay)'],
  8.     ['Integration Test', '2026-09-20', '—', 'In Progress (85%)'],
  9.     ['Mid-term Review', '2026-09-28', '2026-09-28', 'Passed'],
  10. ]
  11. for i in range(6):
  12.     for j in range(4):
  13.         cell = table.Rows[i].Cells[j]
  14.         para = cell.AddParagraph()
  15.         txtRange = para.AppendText(data[i][j])
  16.         if i == 0:
  17.             txtRange.CharacterFormat.Bold = True
  18. navigator = BookmarksNavigator(doc)
  19. navigator.MoveToBookmark('MilestoneTable')
  20. part = TextBodyPart(doc)
  21. part.BodyItems.Add(table)
  22. navigator.ReplaceBookmarkContent(part)
  23. doc.SaveToFile('step3_table.docx', FileFormat.Docx)
  24. doc.Close()
复制代码

Table(doc, True) 的第二个参数启用固定布局模式,列宽由 ResetCells 自动分配。TextBodyPart 用于承载表格、多段落等结构化内容。

三、在书签位置插入图片

AppendPicture 必须在段落上调用,而书签位置不一定有现成空段落。可先在临时 section 中创建带图片的段落,再用 InsertParagraph 插入书签位置,最后移除临时 section:
  1. doc = Document()
  2. doc.LoadFromFile('step3_table.docx')
  3. bn = BookmarksNavigator(doc)
  4. bn.MoveToBookmark('Signature', True, True)
  5. section0 = doc.AddSection()
  6. paragraph = section0.AddParagraph()
  7. paragraph.Format.HorizontalAlignment = HorizontalAlignment.Center
  8. picture = paragraph.AppendPicture('signature.png')
  9. picture.Width = 150.0
  10. picture.Height = 150.0
  11. bn.InsertParagraph(paragraph)
  12. doc.Sections.Remove(section0)
  13. doc.SaveToFile('step4_image.docx', FileFormat.Docx)
  14. doc.Close()
复制代码

MoveToBookmark 的后两个 True 分别控制是否区分大小写、是否向前搜索。Width 和 Height 单位为磅,150 磅约等于 5.3 厘米,适合签章图片;横幅图片可按比例设置,如 Width=400.0、Height=100.0。

四、提取与列举书签内容

document.Bookmarks 是书签集合,支持按索引和按名称访问:
  1. doc = Document()
  2. doc.LoadFromFile('step4_image.docx')
  3. for i in range(doc.Bookmarks.Count):
  4.     bm = doc.Bookmarks[i]
  5.     print(f'{i+1}. {bm.Name}')
  6. bookmark = doc.Bookmarks['ProjectName']
  7. print(bookmark.Name)
复制代码

GetBookmarkContent 返回书签内容对象,遍历 BodyItems 可取出文本。若书签包含表格,还需用 isinstance 判断 Table 类型:
  1. navigator = BookmarksNavigator(doc)
  2. targets = ['ProjectName', 'ClientName', 'Summary', 'RiskLevel', 'RiskDetail']
  3. for name in targets:
  4.     navigator.MoveToBookmark(name)
  5.     content = navigator.GetBookmarkContent()
  6.     text = ''
  7.     for i in range(content.BodyItems.Count):
  8.         item = content.BodyItems.get_Item(i)
  9.         if isinstance(item, Paragraph):
  10.             for j in range(item.ChildObjects.Count):
  11.                 child = item.ChildObjects.get_Item(j)
  12.                 if isinstance(child, TextRange):
  13.                     text += child.Text
  14.     display = text[:80] + '...' if len(text) > 80 else text
  15.     print(f'[{name}] {display}')
复制代码

五、删除书签与书签内容

只删除书签标记、保留内容,可用 Bookmarks.Remove:
  1. doc = Document()
  2. doc.LoadFromFile('step4_image.docx')
  3. bookmark = doc.Bookmarks['RiskSection']
  4. doc.Bookmarks.Remove(bookmark)
  5. doc.SaveToFile('step6a_remove_mark.docx', FileFormat.Docx)
  6. doc.Close()
复制代码

删除后剩余 9 个书签,适合报告定稿时清理标记。若要同时删除书签及其内容,需要定位 BookmarkStart 和 BookmarkEnd 在段落子对象中的索引,再循环移除两者之间的元素:
  1. bookmark = doc.Bookmarks['Signature']
  2. para = bookmark.BookmarkStart.Owner
  3. startIndex = para.ChildObjects.IndexOf(bookmark.BookmarkStart)
  4. para2 = bookmark.BookmarkEnd.Owner
  5. endIndex = para2.ChildObjects.IndexOf(bookmark.BookmarkEnd)
  6. for i in range(startIndex + 1, endIndex):
  7.     para.ChildObjects.RemoveAt(startIndex + 1)
  8. doc.Bookmarks.Remove(bookmark)
  9. doc.SaveToFile('step6b_remove_content.docx', FileFormat.Docx)
  10. 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 按名称查找;删除元素时注意集合索引变化;图片尺寸单位为磅。
回复

使用道具 举报

发表于 2026-10-3 19:00:00 | 显示全部楼层

Re: Python操作Word书签:Free Spire.Doc替换与插图

感谢分享,步骤写得很清楚。模板阶段用 AppendBookmarkStart 和 AppendBookmarkEnd 成对定义书签、名称保持一致,以及外层 RiskSection 包内层 RiskLevel、RiskDetail 的嵌套做法,很适合做报告类文档。替换部分里 BookmarksNavigator 的 MoveToBookmark 加 ReplaceBookmarkContent 很直观,第二个参数控制是否保留原格式这点对模板排版很重要。表格先建 Table 再用 TextBodyPart 包装传入,也让人知道书签里不只是能填文本。 不过帖子最后 Milestone 表格好像没贴完,标题提到的插图部分也还没看到。如果方便的话,想继续看图片插入书签以及嵌套书签整体替换的完整示例。
回复 支持 反对

使用道具 举报

发表于 2026-10-3 19:10:00 | 显示全部楼层

Re: Python操作Word书签:Free Spire.Doc替换与插图

书签这套思路确实比全文查找替换稳得多,模板改版之后不用重写定位逻辑,这点很关键。 有几个地方想请教一下: 一是 ReplaceBookmarkContent 执行完之后,书签本身还在不在?如果还在,那同一份 document 上依次替换其余书签应该不会有冲突;但要是书签被吃掉,后面再有引用同一位置的操作就得改顺序了。之前用别的库踩过这个坑,替换完定位就失效。 二是嵌套书签那段,内层能独立定位这点很有用。不过如果先把外层 RiskSection 整体替换了,里层的 RiskLevel 和 RiskDetail 还能再进去改吗,还是必须从内到外按顺序来?感觉这个顺序问题很容易在生产里出岔子。 三是建议模板本身也脚本生成,别让人在 Word 里手工调。手工编辑最容易动的就是书签名字,改一个字符或者误删一个,运行时直接报找不到,排查起来还绕。 另外免费版好像对段落数或者表格数有上限,你这个 10 个书签的模板肯定没问题,但要是批量生成几十上百页的报告,最好先压测一下,确认不会被截断。 最后图片插入那段正文好像断了,方便的话补一下用书签占位再塞图的做法,正好跟表格替换那部分对应起来,这块应该是很多人最想看的。
回复 支持 反对

使用道具 举报

发表于 2026-10-3 19:20:00 | 显示全部楼层

Re: Python操作Word书签:Free Spire.Doc替换与插图

感谢分享,这套用书签做模板填充的思路很清晰。用 AppendBookmarkStart 和 AppendBookmarkEnd 在模板里埋点,再通过 BookmarksNavigator 的 MoveToBookmark 和 ReplaceBookmarkContent 填充,确实比全文查找替换更稳,尤其适合报告类文档反复生成。 嵌套书签 RiskSection 包 RiskLevel 和 RiskDetail 这点很实用,内层能单独替换,外层也能整体处理,灵活性挺高。替换文本时第二个参数控制是否保留原格式,这个细节讲得也明白,实际做模板时很容易忽略。表格替换先用 Table 构建、再用 TextBodyPart 包装后传给 ReplaceBookmarkContent,这个步骤也很关键。 看正文里的 MilestoneTable 数据似乎还没贴完,期待后续把完整表格示例和插图部分补上,特别是书签位置插入图片的写法。整体很实用,先收藏学习了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-14 11:44 , Processed in 0.040112 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部