You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python-docx处理双语表格时,如何按文档顺序提取Content Control文本?

解决python-docx遍历段落时丢失Content Control内容的问题

我正在开发Python脚本,使用python-docx将Word文档的所有段落转换为双语表格,原段落与转换后的表格格式如下:

原段落:
Paragraph 1
Paragraph 2

转换后:

SourceTarget
Paragraph 1Paragraph 1
Paragraph 2Paragraph 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 04:25:59