Вы вставляете фильтр [text()]
, который явно запрашивает только элементы, у которых есть текстовые узлы ... и тогда вы недовольны, когда он не дает вам элементы без текстовых узлов?
Оставьте этот фильтр, и вы получите элемент модели:
>>> s='''
... <root>
... <Car>
... <Color>Blue</Color>
... <Make>Chevy</Make>
... <Model/>
... </Car>
... </root>'''
>>> e = lxml.etree.fromstring(s)
>>> carData = e.xpath('Car/node()')
>>> carData
[<Element Color at 0x23a5460>, <Element Make at 0x23a54b0>, <Element Model at 0x23a5500>]
>>> dict(((e.tag, e.text) for e in carData))
{'Color': 'Blue', 'Make': 'Chevy', 'Model': None}
Тем не менее, если вашей непосредственной целью является итерация по узлам в дереве, вы можете вместо этого использовать lxml.etree.iterparse()
, что позволит избежать попыток построить полное дерево DOM в памяти и в противном случае будет гораздо более эффективным, чем создание дерево, а затем перебирать его с помощью XPath. (Подумайте SAX, но без безумного и болезненного API).
Реализация с iterparse
может выглядеть так:
def get_cars(infile):
in_car = False
current_car = {}
for (event, element) in lxml.etree.iterparse(infile, events=('start', 'end')):
if event == 'start':
if element.tag == 'Car':
in_car = True
current_car = {}
continue
if not in_car: continue
if element.tag == 'Car':
yield current_car
continue
current_car[element.tag] = element.text
for car in get_cars(infile = cStringIO.StringIO('''<root><Car><Color>Blue</Color><Make>Chevy</Make><Model/></Car></root>''')):
print car
... это больше кода, но (если бы мы не использовали StringIO для примера), он мог бы обработать файл намного больше, чем мог поместиться в памяти.