2014-06-05 20 views
7

varsayalım Aşağıdaki XML belgesi vardır: SonraBıkmadan

<species> 
    Mammals: <dog/> <cat/> 
    Reptiles: <snake/> <turtle/> 
    Birds: <seagull/> <owl/> 
</species> 

böyle species eleman almak:

Şimdi
import lxml.etree 
doc = lxml.etree.fromstring(xml) 
species = doc.xpath('/species')[0] 

Bir yazdırmak istiyorum türler tarafından gruplandırılmış hayvanların listesi. ElementTree API'yi kullanarak nasıl yapabilirim? Eğer düğüm tüm numaralandırırsanız

+0

sağınızda üzerinde bakarsak ... o –

+0

Eğer xml formatında kontrolünü var ... doğru yönde işaret etmelidir aşağı ilgili altındaki 4 benziyor?Normal olarak, Memeliler, vb. Gibi sınıflandırıcılar, xpath seçicilerinin kolayca yazılabilmesi için xml eleman isimleri veya öznitelikleri (ör. ) olarak ifade edilir. – tdelaney

+0

Hayır, XML'yi değiştiremiyorum. – Alicia

cevap

4

, sen türlerle eleman düğümlerine, ardından sınıf ile bir metin düğümü görürsünüz:

>>> for node in species.xpath("child::node()"): 
...  print type(node), node 
... 
<class 'lxml.etree._ElementStringResult'> 
    Mammals: 
<type 'lxml.etree._Element'> <Element dog at 0xe0b3c0> 
<class 'lxml.etree._ElementStringResult'> 
<type 'lxml.etree._Element'> <Element cat at 0xe0b410> 
<class 'lxml.etree._ElementStringResult'> 
    Reptiles: 
<type 'lxml.etree._Element'> <Element snake at 0xe0b460> 
<class 'lxml.etree._ElementStringResult'> 
<type 'lxml.etree._Element'> <Element turtle at 0xe0b4b0> 
<class 'lxml.etree._ElementStringResult'> 
    Birds: 
<type 'lxml.etree._Element'> <Element seagull at 0xe0b500> 
<class 'lxml.etree._ElementStringResult'> 
<type 'lxml.etree._Element'> <Element owl at 0xe0b550> 
<class 'lxml.etree._ElementStringResult'> 

Yani oradan inşa edebilirsiniz:

my_species = {} 
current_class = None 
for node in species.xpath("child::node()"): 
    if isinstance(node, lxml.etree._ElementStringResult): 
     text = node.strip(' \n\t:') 
     if text: 
      current_class = my_species.setdefault(text, []) 
    elif isinstance(node, lxml.etree._Element): 
     if current_class is not None: 
      current_class.append(node.tag) 
print my_species 

{'Mammals': ['dog', 'cat'], 'Reptiles': ['snake', 'turtle'], 'Birds': ['seagull', 'owl']} 

yılında

sonuçları bu metin düğümleri nasıl düzenlendiğine tüm kırılgan ... küçük değişiklikler olduğunu ayrışmayı bozabilir.

+0

Bunu beğendim, basit bir XPath :) – Alicia

+0

@alecxe - Bir önceki metin düğümlerinin giderek artan bir sayısını işlemek ve her seferinde sonuncusu atıyorsunuz ... Çözümümün daha basit olduğunu düşünüyorum. – tdelaney

+0

Python 3'te, metin düğümünün türü 'lxml.etree._ElementUnicodeResult'. – saaj

2

Tasarım notu

@tdelaney tarafından cevap temelde doğru, ama Python eleman ağaç API biri nüans işaret etmek istiyorum. İşte the lxml tutorial bir alıntı:

Elemanları metin içerebilir:

<root>TEXT</root> 

sayıda XML belgeleri (veri merkezli belgelerde), bu metin bulunabilir tek yerdir. Ağaç hiyerarşisinin en altındaki bir yaprak etiketi ile kapsüllenir. , <br/> etikettir İşte

<html><body>Hello<br/>World</body></html> 

: XML böyle (X) HTML olarak etiketlenmiş metin belgeleri için kullanılırsa

Ancak, metni de doğru ağacın ortasında, farklı öğeler arasında görünebilir metinle çevrili Bu genellikle belge stili veya karma içerikli XML olarak adlandırılır. Öğeler, bunları tail özelliğiyle destekler. XML ağacındaki bir sonraki öğeye kadar öğeyi doğrudan izleyen metni içerir.

text ve tail iki özelliği, bir XML belgesindeki herhangi bir metin içeriğini temsil etmek için yeterlidir. Bu şekilde, ElementTree API'si , Öğe sınıfına ek olarak, (klasik DOM API'lerinden bildiğiniz gibi) sık sık yol alma eğiliminde olan özel bir metin düğümleri gerektirmez. o çıkış metin düğümlerine ağaç zorlamadan belge metni almak mümkündür dikkate alarak bu özellikleri

Uygulama

.

#!/usr/bin/env python3.3 


import itertools 
from pprint import pprint 

try: 
    from lxml import etree 
except ImportError: 
    from xml.etree import cElementTree as etree 


def textAndElement(node): 
    '''In py33+ recursive generators are easy''' 

    yield node 

    text = node.text.strip() if node.text else None 
    if text: 
    yield text 

    for child in node: 
    yield from textAndElement(child) 

    tail = node.tail.strip() if node.tail else None 
    if tail: 
    yield tail 


if __name__ == '__main__': 
    xml = ''' 
    <species> 
     Mammals: <dog/> <cat/> 
     Reptiles: <snake/> <turtle/> 
     Birds: <seagull/> <owl/> 
    </species> 
    ''' 
    doc = etree.fromstring(xml) 

    pprint(list(textAndElement(doc))) 
    #[<Element species at 0x7f2c538727d0>, 
    #'Mammals:', 
    #<Element dog at 0x7f2c538728c0>, 
    #<Element cat at 0x7f2c53872910>, 
    #'Reptiles:', 
    #<Element snake at 0x7f2c53872960>, 
    #<Element turtle at 0x7f2c538729b0>, 
    #'Birds:', 
    #<Element seagull at 0x7f2c53872a00>, 
    #<Element owl at 0x7f2c53872a50>] 

    gen = textAndElement(doc) 
    next(gen) # skip root 
    groups = [] 
    for _, g in itertools.groupby(gen, type): 
    groups.append(tuple(g)) 

    pprint(dict(zip(*[iter(groups)] * 2))) 
    #{('Birds:',): (<Element seagull at 0x7fc37f38aaa0>, 
    #    <Element owl at 0x7fc37f38a820>), 
    #('Mammals:',): (<Element dog at 0x7fc37f38a960>, 
    #    <Element cat at 0x7fc37f38a9b0>), 
    #('Reptiles:',): (<Element snake at 0x7fc37f38aa00>, 
    #    <Element turtle at 0x7fc37f38aa50>)}