2014-09-20 4 views
5

я получить XML-документы таким образом:Проверьте элемент имеет детей или не

import xml.etree.ElementTree as ET 

root = ET.parse(urllib2.urlopen(url)) 
for child in root.findall("item"): 
    a1 = child[0].text # ok 
    a2 = child[1].text # ok 
    a3 = child[2].text # ok 
    a4 = child[3].text # BOOM 
    # ... 

XML-выглядит следующим образом:

<item> 
    <a1>value1</a1> 
    <a2>value2</a2> 
    <a3>value3</a3> 
    <a4> 
    <a11>value222</a11> 
    <a22>value22</a22> 
    </a4> 
</item> 

Как проверить, если a4 (в данном конкретном случае, но это мог быть любой другой элемент) есть дети?

ответ

6

Вы можете попробовать функцию list на элементе:

>>> xml = """<item> 
    <a1>value1</a1> 
    <a2>value2</a2> 
    <a3>value3</a3> 
    <a4> 
    <a11>value222</a11> 
    <a22>value22</a22> 
    </a4> 
</item>""" 
>>> root = ET.fromstring(xml) 
>>> list(root[0]) 
[] 
>>> list(root[3]) 
[<Element 'a11' at 0x2321e10>, <Element 'a22' at 0x2321e48>] 
>>> len(list(root[3])) 
2 
>>> print "has children" if len(list(root[3])) else "no child" 
has children 
>>> print "has children" if len(list(root[2])) else "no child" 
no child 
>>> # Or simpler, without a call to list within len, it also works: 
>>> print "has children" if len(root[3]) else "no child" 
has children 

Я изменил ваш образец, потому что вызов функции findall в корне item не работал (поскольку findall будет искать прямых потомков, а не текущий элемент). Если вы хотите получить доступ текст subchildren позже в рабочей программе, вы можете сделать:

for child in root.findall("item"): 
    # if there are children, get their text content as well. 
    if len(child): 
    for subchild in child: 
     subchild.text 
    # else just get the current child text. 
    else: 
    child.text 

Это было бы хорошо подходит для рекурсивной, хотя.

+0

не работает. Не могли бы вы использовать мой пример с итерацией? –

+1

не работает, потому что ваш цикл итерации не содержит элементов, поскольку нет элементов с именем «item» – marscher

+0

да, это дает их в моем реальном приложении. –

0

Класс элемента имеет метод get children. Таким образом, вы должны использовать что-то вроде этого, чтобы проверить, есть ли дети и сохранить результат в словаре по имени ключа = тега:

result = {} 
for child in root.findall("item"): 
    is child.getchildren() == []: 
     result[child.tag] = child.text 
+0

'getchildren' устарел, хотя с версии 2.7. [Из документации] (https://docs.python.org/2/library/xml.etree.elementtree.html): Используйте список (elem) или итерацию. – jlr

+0

Вы правы. Он больше не должен использоваться – marscher

0

Я лично рекомендовал бы использовать XML-парсер, который полностью поддерживает выражения xpath. Для таких задач недостаточно subset supported by xml.etree.

Например, в lxml я могу сделать:

"дают мне все дети детей из <item> узла":

doc.xpath('//item/*/child::*') #equivalent to '//item/*/*', if you're being terse 
Out[18]: [<Element a11 at 0x7f60ec1c1348>, <Element a22 at 0x7f60ec1c1888>] 

или,

«дать мне все <item> дети, у которых нет детей »:

doc.xpath('/item/*[count(child::*) = 0]') 
Out[20]: 
[<Element a1 at 0x7f60ec1c1588>, 
<Element a2 at 0x7f60ec1c15c8>, 
<Element a3 at 0x7f60ec1c1608>] 

или

«дает мне все элементы, которые не имеют каких-либо детей»:

doc.xpath('//*[count(child::*) = 0]') 
Out[29]: 
[<Element a1 at 0x7f60ec1c1588>, 
<Element a2 at 0x7f60ec1c15c8>, 
<Element a3 at 0x7f60ec1c1608>, 
<Element a11 at 0x7f60ec1c1348>, 
<Element a22 at 0x7f60ec1c1888>] 

# and if I only care about the text from those nodes... 
doc.xpath('//*[count(child::*) = 0]/text()') 
Out[30]: ['value1', 'value2', 'value3', 'value222', 'value22'] 
+0

Предлагая lxml предполагает, что проблема с производительностью и отсутствием функций xpath. Это определенно лучше, чем ElementTree, но я бы не пошел таким путем, если нет проблем с последним, особенно учитывая, что lxml требует установки, и это не всегда хорошая прогулка по парку. – jlr

+1

Производительность - это вещь, да, но полная поддержка xpath означает, что вы выполняете всю работу по выбору узлов в одном компактном месте. Запросы xpath берут меня на несколько секунд, чтобы написать; пишущий код python, чтобы ходить по дереву и выбирать узлы, которые я хочу, занимают больше времени и гораздо вероятнее создавать ошибки. Есть много преимуществ, кроме производительности. – roippi

2

Самый простой способ, который я смог найти, чтобы использовать значение элемента bool непосредственно. Это означает, что вы можете использовать a4 в условном операторе как есть:

a4 = Element('a4') 
if a4: 
    print('Has kids') 
else: 
    print('No kids yet') 

a4.append(Element('x')) 
if a4: 
    print('Has kids now') 
else: 
    print('Still no kids') 

Выполнение этого кода напечатает

No kids yet 
Has kids now 

Булево значение элемента не говорит ничего о text, tail или атрибутах. Это указывает только на наличие или отсутствие детей, о чем спрашивал первоначальный вопрос.

Смежные вопросы