2014-09-20 49 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 

我修改了你的示例,因爲item根上的findall函數調用不起作用(因爲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

不起作用。你能用我的例子迭代嗎? – 2014-09-20 16:28:46

+1

它不起作用,因爲你的迭代循環沒有產生任何元素,因爲沒有元素名爲'item' – marscher 2014-09-20 16:36:01

+0

是的,它在我的真實應用程序中產生它們。 – 2014-09-20 16:43:49

0

元素類具有get兒童方法。所以,你應該使用這樣的事情,要檢查是否有孩子,結果存儲在字典中的鍵=標籤名稱:

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 2014-09-20 16:15:14

+0

你是對的。它不應該再使用 – marscher 2014-09-20 16:16:18

0

我個人建議您使用完全支持xpath表達式的xml解析器。 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 2014-09-20 17:47:56

+1

性能是一件事,是的,但完整的xpath支持意味着您可以在一個緊湊的地方完成所有選擇節點的工作。 xpath查詢需要幾秒鐘的時間才能完成;編寫python代碼來遍歷樹並選擇我想要的節點需要更長的時間,並且更有可能產生錯誤。除了表演,還有很多好處。 – roippi 2014-09-20 17:56:18

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 

元素的布爾值並沒有說任何有關texttail或屬性。它只是表明是否存在兒童,這是原始問題所要求的。

相關問題