Python-docx处理双语表格时,如何按文档顺序提取Content Control文本?
解决python-docx遍历段落时丢失Content Control内容的问题
我正在开发Python脚本,使用python-docx将Word文档的所有段落转换为双语表格,原段落与转换后的表格格式如下:
原段落:
Paragraph 1
Paragraph 2转换后:
Source Target Paragraph 1 Paragraph 1 Paragraph 2 Paragraph 2
编写的BilingualText函数能处理Run和Hyperlink,但iter_inner_content()无法识别Rich Text或Plain Text Content Control,导致这些位置内容空白或段落被跳过。需要按文档顺序提取内容控件内的文本,转为表格中的普通文本Run,无需保留Content Control本身。
解决方案
要处理Content Control,需直接遍历段落的XML元素(Par._p),识别其中的<w:sdt>节点(内容控件),提取内部文本和格式信息后转为普通Run添加到表格单元格中。
关键修改点
- 将
Doc.paragraphs转为列表遍历,避免修改文档结构时引发遍历异常 - 直接遍历段落的XML子元素,覆盖Run、Hyperlink和Content Control三种类型
- 解析Content Control的
<w:sdtContent>区域,提取内部Run元素并复制格式与文本
修改后的完整代码
from docx import Document from docx.oxml.ns import qn from docx.text.paragraph import Paragraph from docx.table import Table, _Cell from docx.text.run import Run from docx.text.hyperlink import Hyperlink # 假设你已实现以下辅助函数 def CopyParagraphFormatting(target_par: Paragraph, source_par: Paragraph): target_par.style = source_par.style target_par.alignment = source_par.alignment target_par.paragraph_format.line_spacing = source_par.paragraph_format.line_spacing # 按需添加其他段落格式复制逻辑 def CopyRunFormatting(target_run: Run, source_run: Run): target_run.font.bold = source_run.font.bold target_run.font.italic = source_run.font.italic target_run.font.underline = source_run.font.underline target_run.font.size = source_run.font.size target_run.font.name = source_run.font.name # 按需添加其他Run格式复制逻辑 def AddHyperlink(paragraph: Paragraph, url: str, text: str) -> Hyperlink: part = paragraph.part r_id = part.relate_to(url, qn('rIdHyperlink'), is_external=True) hyperlink = paragraph.add_run(text)._element hyperlink.tag = qn('w:hyperlink') hyperlink.set(qn('r:id'), r_id) return Hyperlink(hyperlink, paragraph) def BilingualText(Doc: Document, SourceRight: bool) -> None: """Loops through all paragraphs in a doc and turns them into a bilingual table. Basically emulates the 'Convert To Table' function in Word.""" # 先转成列表,避免遍历中修改文档结构导致的异常 paragraphs = list(Doc.paragraphs) for Par in paragraphs: # 跳过空段落(可根据需求调整) if not Par.text.strip(): continue # 添加新表格并移至段落前方 Tbl = Doc.add_table(1, 2) Par._p.addprevious(Tbl._element) LeftCell: _Cell = Tbl.cell(0, 0) RightCell: _Cell = Tbl.cell(0, 1) LeftPar: Paragraph = LeftCell.paragraphs[0] RightPar: Paragraph = RightCell.paragraphs[0] # 复制段落格式 CopyParagraphFormatting(LeftPar, Par) CopyParagraphFormatting(RightPar, Par) # 遍历段落所有XML子元素,处理各类内容 for element in Par._p: # 处理普通Run if element.tag == qn('w:r'): source_run = Run(element, Par) LeftRun = LeftPar.add_run(source_run.text) CopyRunFormatting(LeftRun, source_run) RightRun = RightPar.add_run(source_run.text) CopyRunFormatting(RightRun, source_run) if SourceRight: RightRun.font.hidden = True else: LeftRun.font.hidden = True # 处理超链接 elif element.tag == qn('w:hyperlink'): source_hpl = Hyperlink(element, Par) LeftHpl = AddHyperlink(LeftPar, source_hpl.address, source_hpl.text) RightHpl = AddHyperlink(RightPar, source_hpl.address, source_hpl.text) if SourceRight: for run in RightHpl.runs: run.font.hidden = True else: for run in LeftHpl.runs: run.font.hidden = True # 处理Content Control(SDT) elif element.tag == qn('w:sdt'): sdt_content = element.find(qn('w:sdtContent')) if not sdt_content: continue # 遍历内容控件内的所有Run for sdt_r_element in sdt_content.findall(qn('w:r')): sdt_run = Run(sdt_r_element, Par) LeftRun = LeftPar.add_run(sdt_run.text) CopyRunFormatting(LeftRun, sdt_run) RightRun = RightPar.add_run(sdt_run.text) CopyRunFormatting(RightRun, sdt_run) if SourceRight: RightRun.font.hidden = True else: LeftRun.font.hidden = True # 删除原段落 Par._p.getparent().remove(Par._p)
内容的提问来源于stack exchange,提问作者I like Bananas
相关产品推荐
相关产品推荐

