XML-фильтрация с помощью Python - PullRequest
5 голосов
/ 20 мая 2010

У меня есть следующий XML-документ:

<node0>
    <node1>
      <node2 a1="x1"> ... </node2>
      <node2 a1="x2"> ... </node2>
      <node2 a1="x1"> ... </node2>
    </node1>
</node0>

Я хочу отфильтровать node2, когда a1="x2". Пользователь предоставляет значения xpath и атрибутов, которые необходимо проверить и отфильтровать. Я посмотрел на некоторые решения в Python, такие как BeautifulSoup, но они слишком сложны и не сохраняют текст. Я хочу сохранить документ таким же, как и раньше, с некоторыми отфильтрованными материалами.

Можете ли вы порекомендовать простое и краткое решение? Это не должно быть слишком сложным с виду. Фактический XML-документ не так прост, как указано выше, но идея та же.

1 Ответ

6 голосов
/ 20 мая 2010

Используется xml.etree.ElementTree, который находится в стандартной библиотеке:

import xml.etree.ElementTree as xee
data='''\
<node1>
  <node2 a1="x1"> ... </node2>
  <node2 a1="x2"> ... </node2>
  <node2 a1="x1"> ... </node2>
</node1>
'''
doc=xee.fromstring(data)

for tag in doc.findall('node2'):
    if tag.attrib['a1']=='x2':
        doc.remove(tag)
print(xee.tostring(doc))
# <node1>
#   <node2 a1="x1"> ... </node2>
#   <node2 a1="x1"> ... </node2>
# </node1>

При этом используется lxml, который отсутствует в стандартной библиотеке, но имеет более мощный синтаксис :

import lxml.etree
data='''\
<node1>
  <node2 a1="x1"> ... </node2>
  <node2 a1="x2"> ... </node2>
  <node2 a1="x1"> ... </node2>
</node1>
'''
doc = lxml.etree.XML(data)
e=doc.find('node2/[@a1="x2"]')
doc.remove(e)
print(lxml.etree.tostring(doc))

# <node1>
#   <node2 a1="x1"> ... </node2>
#   <node2 a1="x1"> ... </node2>
# </node1>

Редактировать: Если node2 более глубоко скрыт в XML, то вы можете перебрать все теги, проверить каждый родительский тег, чтобы увидеть, является ли элемент node2 одним из его дочерних элементов, и удалите это, если так:

Использование только xml.etree.ElementTree:

doc=xee.fromstring(data)
for parent in doc.getiterator():
    for child in parent.findall('node2'):
        if child.attrib['a1']=='x2':
            parent.remove(child)

Использование lxml:

doc = lxml.etree.XML(data)
for parent in doc.iter('*'):
    child=parent.find('node2/[@a1="x2"]')
    if child is not None:
        parent.remove(child)
...