如何使用 Python 中的 ElementTree 删除带有 parents 的 iterparse 的 XML 部分?
How can I remove XML parts with iterparse with parents included using ElementTree in Python?
我有多个大文件需要导入并遍历它们 - 所有文件都是 xml 并且具有相同的树结构。
结构是这样的,除了 ID 之外还有一些额外的文本,所以在 Start 下有更多的 children 元素标签:
我想做的是输入一个我知道是错误的 ID 列表,然后从整个 XML 文件中删除该报告。一份报告介于两个 "T" 之间。
<Header>
<Header2>
<Header3>
<T>
<Start>
<Id>abcd</Id>
</Start>
</T>
<T>
<Start>
<Id>qrlf</Id>
</Start>
</T>
</Header3>
</Header2>
</Header>
我目前拥有的:
from xml.etree import cElementTree as ET
file_path = '/path/to/my_xml.xml'
to_remove = []
root = None
for event, elem in ET.iterparse(file_path, events=("start", "end")):
if event == 'end':
if elem.tag == 'Id':
new_root = elem
#print([elem.tag for elem in new_root.iter()])
for elem2 in new_root.iter('Id'):
id = elem2.text
if id =='abcd':
print(id)
to_remove.append(new_root)
root = elem
for item in to_remove:
root.remove(item)
所以上面的代码显然不起作用,因为根是以 Header 开头的整个 xml 文件,它无法准确找到我要删除的子元素,因为它的 parent 是 Header3 而不是 Header。
所以期望的输出是:
<Header>
<Header2>
<Header3>
<T>
<Start>
<Id>qrlf</Id>
</Start>
</T>
</Header3>
</Header2>
</Header>
展望未来,我要删除的不是单个值,而是数千个值,因此将成为一个列表,我只是认为以这种方式表示问题更容易。
感谢任何帮助。
由于您的 XML 结构很简单,因此使用 Xpath 可能更容易(大约下降 https://docs.python.org/3/library/xml.etree.elementtree.html 的 1/3)。以下是文档页面该部分的用法示例:
import xml.etree.ElementTree as ET
root = ET.fromstring(countrydata)
# Top-level elements
root.findall(".")
# All 'neighbor' grand-children of 'country' children of the top-level
# elements
root.findall("./country/neighbor")
# Nodes with name='Singapore' that have a 'year' child
root.findall(".//year/..[@name='Singapore']")
# 'year' nodes that are children of nodes with name='Singapore'
root.findall(".//*[@name='Singapore']/year")
# All 'neighbor' nodes that are the second child of their parent
root.findall(".//neighbor[2]")
可以在文档页面的顶部找到用于示例的 XML 结构。
第二个示例显示了一种简单的方法来 select 您想要删除的子元素("T" 在您的情况下)但在您的情况下,倒数第二个情况可能更有用。但是请参阅示例下方出现的 Xpath 语法部分中的 [tag='text'] 操作。
将该操作的结果发送到删除操作(页面下方 ~3/4),然后是 XML 树写入操作(页面下方 ~4/5)以获取清理后的 XML。
上面假设你传递的是一个字符串,你必须使用解析从文件输入,例如:
import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()
** 免责声明 *** 我正在做类似的工作,但我实际上并没有尝试这样做。因此,将此视为灵感,而不是完整的解决方案。
顺便说一句,我正在使用 python 3.7.4。对于那些还不知道的人,您可以使用版本 select 或在文档页面的左上角 select 您正在使用的版本。
我觉得你可以用
ids_to_remove = ['abcd']
elements_to_remove = []
for event, element in ET.iterparse('file.xml'):
if element.tag == 'T' and element.find('Start/Id').text in ids_to_remove:
elements_to_remove.append(element)
if element.tag == 'Header3':
for el in elements_to_remove:
element.remove(el)
el.clear()
if element.tag == 'Header':
root = element
ET.dump(root)
我还没有测试过它是如何处理大文件的,显然它首先收集所有要删除的元素,最后删除它们,我不确定 ElementTree API 中是否有分离 element
在 if element.tag == 'T' and element.find('Start/Id').text in ids_to_remove:
分支中,也许以下会更早地释放元素:
ids_to_remove = ['abcd', 'baz', 'bar']
for event, element in ET.iterparse('file.xml', events = ['start', 'end']):
if event == 'end' and element.tag == 'T' and element.find('Start/Id').text in ids_to_remove:
header3.remove(element)
element.clear()
if event == 'start' and element.tag == 'Header3':
header3 = element;
if element.tag == 'Header':
root = element
ET.dump(root)
我有多个大文件需要导入并遍历它们 - 所有文件都是 xml 并且具有相同的树结构。 结构是这样的,除了 ID 之外还有一些额外的文本,所以在 Start 下有更多的 children 元素标签: 我想做的是输入一个我知道是错误的 ID 列表,然后从整个 XML 文件中删除该报告。一份报告介于两个 "T" 之间。
<Header>
<Header2>
<Header3>
<T>
<Start>
<Id>abcd</Id>
</Start>
</T>
<T>
<Start>
<Id>qrlf</Id>
</Start>
</T>
</Header3>
</Header2>
</Header>
我目前拥有的:
from xml.etree import cElementTree as ET
file_path = '/path/to/my_xml.xml'
to_remove = []
root = None
for event, elem in ET.iterparse(file_path, events=("start", "end")):
if event == 'end':
if elem.tag == 'Id':
new_root = elem
#print([elem.tag for elem in new_root.iter()])
for elem2 in new_root.iter('Id'):
id = elem2.text
if id =='abcd':
print(id)
to_remove.append(new_root)
root = elem
for item in to_remove:
root.remove(item)
所以上面的代码显然不起作用,因为根是以 Header 开头的整个 xml 文件,它无法准确找到我要删除的子元素,因为它的 parent 是 Header3 而不是 Header。
所以期望的输出是:
<Header>
<Header2>
<Header3>
<T>
<Start>
<Id>qrlf</Id>
</Start>
</T>
</Header3>
</Header2>
</Header>
展望未来,我要删除的不是单个值,而是数千个值,因此将成为一个列表,我只是认为以这种方式表示问题更容易。 感谢任何帮助。
由于您的 XML 结构很简单,因此使用 Xpath 可能更容易(大约下降 https://docs.python.org/3/library/xml.etree.elementtree.html 的 1/3)。以下是文档页面该部分的用法示例:
import xml.etree.ElementTree as ET
root = ET.fromstring(countrydata)
# Top-level elements
root.findall(".")
# All 'neighbor' grand-children of 'country' children of the top-level
# elements
root.findall("./country/neighbor")
# Nodes with name='Singapore' that have a 'year' child
root.findall(".//year/..[@name='Singapore']")
# 'year' nodes that are children of nodes with name='Singapore'
root.findall(".//*[@name='Singapore']/year")
# All 'neighbor' nodes that are the second child of their parent
root.findall(".//neighbor[2]")
可以在文档页面的顶部找到用于示例的 XML 结构。
第二个示例显示了一种简单的方法来 select 您想要删除的子元素("T" 在您的情况下)但在您的情况下,倒数第二个情况可能更有用。但是请参阅示例下方出现的 Xpath 语法部分中的 [tag='text'] 操作。
将该操作的结果发送到删除操作(页面下方 ~3/4),然后是 XML 树写入操作(页面下方 ~4/5)以获取清理后的 XML。
上面假设你传递的是一个字符串,你必须使用解析从文件输入,例如:
import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()
** 免责声明 *** 我正在做类似的工作,但我实际上并没有尝试这样做。因此,将此视为灵感,而不是完整的解决方案。
顺便说一句,我正在使用 python 3.7.4。对于那些还不知道的人,您可以使用版本 select 或在文档页面的左上角 select 您正在使用的版本。
我觉得你可以用
ids_to_remove = ['abcd']
elements_to_remove = []
for event, element in ET.iterparse('file.xml'):
if element.tag == 'T' and element.find('Start/Id').text in ids_to_remove:
elements_to_remove.append(element)
if element.tag == 'Header3':
for el in elements_to_remove:
element.remove(el)
el.clear()
if element.tag == 'Header':
root = element
ET.dump(root)
我还没有测试过它是如何处理大文件的,显然它首先收集所有要删除的元素,最后删除它们,我不确定 ElementTree API 中是否有分离 element
在 if element.tag == 'T' and element.find('Start/Id').text in ids_to_remove:
分支中,也许以下会更早地释放元素:
ids_to_remove = ['abcd', 'baz', 'bar']
for event, element in ET.iterparse('file.xml', events = ['start', 'end']):
if event == 'end' and element.tag == 'T' and element.find('Start/Id').text in ids_to_remove:
header3.remove(element)
element.clear()
if event == 'start' and element.tag == 'Header3':
header3 = element;
if element.tag == 'Header':
root = element
ET.dump(root)