如何使用python修改xml文件中嵌套元素的文本?

Posted

技术标签:

【中文标题】如何使用python修改xml文件中嵌套元素的文本?【英文标题】:How to modify the text of nested elements in xml file using python? 【发布时间】:2020-02-15 17:33:44 【问题描述】:

目前我正在处理语料库/数据集。它是 xml 格式,如下图所示。我面临一个问题。 我想一一访问所有 ‘ne’ 元素,如下图所示。然后我想访问“ne”元素内的“W”元素的文本。然后我想连接你的符号'SDi'和'EDi'与这些'W'元素的文本。 ‘i’ 可以取任何从 1 开始的正整数。在 ‘SDi’ 的情况下,我只需要 ‘ne’ 元素内的第一个 ‘W’ 元素的文本。在“EDi”的情况下,我只需要“ne”元素内的最后一个“W”元素的文本。 目前我在运行代码后没有得到任何输出。我认为这是因为元素“W”从未被访问过。此外,我认为元素 'W' 未被访问,因为它是元素 'ne' 的孙子,因此不能直接访问它,而是可能在其父节点的帮助下。

注意1:‘ne’元素中子元素的个数和名称不一样。

注2:这里只说明需要的东西。您可能会在编码/图片中找到一些其他细节,但请忽略它们。

我正在使用 Spyder (python 3.6) 任何帮助,将不胜感激。

我正在处理的 XML 文件中的图片如下所示:

XML 文件的文本版本: Click here

示例/预期输出图像(下):

到目前为止我所做的编码:

for i in range(len(List_of_root_nodes)):
true_false = True
current = List_of_root_nodes[i]
start_ID = current.PDante_ID
#print('start:', start_ID)  # For Testing
end_ID = None
number = str(i+1)  # This number will serve as i used with SD and ED that is (SDi and EDi)

discourse_starting_symbol = "SD" + number
discourse_ending_symbol = "ED" + number

while true_false:    
    if current.right_child is None:        
        end_ID = current.PDante_ID
        #print('end:', end_ID)  # For Testing
        true_false = False        
    else:        
        current = current.right_child

# Finding 'ne' element with id='start_ID'
ne_text = None
ne_id = None

for ne in myroot.iter('ne'):    
    ne_id = ne.get('id')

    # If ne_id matches with start_ID means the place where SDi is to be placed is found    
    if ne_id == start_ID:        
        for w in ne.iter('W'):            
            ne_text = str(w.text)            
            boundary_and_text = " " + str(discourse_starting_symbol) + " " + ne_text
            w.text = boundary_and_text
            break

    # If ne_id matches with end_ID means the place where EDi is to be placed is found

    # Some changes Required here: Here the 'EDi' will need to be placed after the last 'W' element.
    # So last 'W' element needs to be accessed
    if ne_id == end_ID:        
        for w in ne.iter('W'):            
            ne_text = str(w.text)            
            boundary_and_text = ne_text + " " + str(discourse_ending_symbol) + " "
            w.text = boundary_and_text
            break

【问题讨论】:

您能否发布您的 xml sn-p 的文本版本或指向它的链接以进行测试?您的预期输出示例也会有所帮助。 我已根据要求编辑了帖子,以便您可以帮助我。 @ColeTierney You should not post code as an image because:... 避免我们下载您的数据。嵌入一​​个小样本(如您的屏幕截图)作为帖子正文中的文本,以便在链接失效时为未来的读者提供服务。 欣赏你所说的,但请尊重,@Parfait 我不认为我已经发布了我的代码(标题为“到目前为止我已经完成的编码”)作为图像。跨度> 【参考方案1】:

类似这样的东西(a.xml 是你上传的 XML):

注意代码没有使用任何外部库。

import xml.etree.ElementTree as ET

SD = 'SD'
ED = 'ED'

root = ET.parse('a.xml')

counter = 1

for ne in root.findall('.//ne'):
    w_lst = ne.findall('.//W')
    if w_lst:
        w_lst[0].text = ' '.format(SD, counter, w_lst[0].text)
        if len(w_lst) > 1:
            w_lst[-1].text = ' '.format(w_lst[-1].text, ED, counter)
        counter += 1
ET.dump(root)

【讨论】:

你能解释一下你发布的代码吗@balderman 当然。代码循环遍历所有元素。对于每个 ne,它会找到 w 个元素​​。如果有一个 w 元素(在 ne 下),它会设置 SD 值。如果有多个,则转到最后一个 w 并设置 ED 值。它对你有用吗? 我 100% 理解你所说的,但由于我的编码几乎为零,因此我不了解每一行的确切工作方式。你能告诉我更多关于' '的三个大括号的信息吗?他们在这里做什么?他们是否提供了一些空闲空间?另外,如果您可以告诉我有关“格式()”的功能。它对传递的三个参数做了什么?将它们结合在一起。是的?最后为什么会有 ET.dump(root)?是因为我们对 xml 文件进行了一些更改,现在需要重新构建/写入/或类似的东西吗? 上次当我检查您发布的解决方案时,我无法理解它,因此我没有检查它是否适合我。但是在考虑了一段时间之后,现在我认为这是我想要的东西,需要更少或更多的更改。我会告诉您它是否对我有用,并将您的解决方案标记为“有效”。谢谢【参考方案2】:

当您需要修改具有各种细微差别的 XML 时,请考虑 XSLT,这是一种专门用于转换 XML 文件的语言。您可以使用 Python 的第三方模块 lxml(不是内置的 etree)运行 XSLT 1.0 脚本。

具体来说,调用identity transform 按原样复制XML,然后运行两个模板将SDI 添加到第一个<W> 的文本中,并将最后一个EDI 添加到最后一个<W> 的文本中。如果有 10 或 10,000 个 <W> 节点,无论是否深度嵌套,解决方案都会起作用。

为了演示 *** 的*** Python 和 XSLT 用户的示例数据,请参阅 online demo,其中 SDIEDI 被添加到第一个和最后一个 <user> 节点:

XSLT (另存为.xsl文件,Python中要加载的特殊.xml文件)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!-- IDENTITY TRANSFORM -->    
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- EDIT FIRST W NODE -->    
  <xsl:template match="W[count(preceding::W)=0]">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:value-of select="concat('SDI ', text())"/>
    </xsl:copy>
  </xsl:template>

  <!-- EDIT LAST W NODE -->    
  <xsl:template match="W[count(preceding::W)+1 = count(//W)]">
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:value-of select="concat('EDI ', text())"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Python (无循环或 if/else 逻辑)

import lxml.etree as et

doc = et.parse('/path/to/Input.xml')
xsl = et.parse('/path/to/Script.xsl')

# CONFIGURE TRANSFORMER
transform = et.XSLT(xsl)    

# TRANSFORM SOURCE DOC
result = transform(doc)

# OUTPUT TO CONSOLE
print(result)

# SAVE TO FILE
with open('Output.xml', 'wb') as f:
    f.write(result)

【讨论】:

以上是关于如何使用python修改xml文件中嵌套元素的文本?的主要内容,如果未能解决你的问题,请参考以下文章

在Python中使用ElementTree API插入xml元素作为嵌套元素。

如何单击元素并从链接的 xml 文件(python)中解析文本?

如何从xml文件中获取嵌套元素

使用 Python 或 XSLT 将复杂的 XML 转换为 CSV

如何使用 python 从 XML 创建嵌套字典?

如何在没有多余换行符的情况下从 BeautifulSoup 输出 XML?