Análisis de tags XML en blanco con LXML y Python

Al analizar documentos XML en el formato de:

 Blue Chevy Camaro  

Yo uso el siguiente código:

 carData = element.xpath('//Root/Foo/Bar/Car/node()[text()]') parsedCarData = [{field.tag: field.text for field in carData} for action in carData] print parsedCarData[0]['Color'] #Blue 

Este código no funcionará si una etiqueta está vacía, como por ejemplo:

  Blue Chevy   

Usando el mismo código que el anterior:

 carData = element.xpath('//Root/Foo/Bar/Car/node()[text()]') parsedCarData = [{field.tag: field.text for field in carData} for action in carData] print parsedCarData[0]['Model'] #Key Error 

¿Cómo puedo analizar esta etiqueta en blanco.

¿Está colocando un filtro [text()] que solo solicita de manera explícita los elementos que tienen nodos de texto … y luego no está contento cuando no le dan elementos sin nodos de texto?

Deja ese filtro fuera, y obtendrás el elemento de tu modelo:

 >>> s=''' ...  ...  ... Blue ... Chevy ...  ...  ... ''' >>> e = lxml.etree.fromstring(s) >>> carData = e.xpath('Car/node()') >>> carData [, , ] >>> dict(((e.tag, e.text) for e in carData)) {'Color': 'Blue', 'Make': 'Chevy', 'Model': None} 

Dicho esto: si su objective inmediato es iterar sobre los nodos del árbol, podría considerar usar lxml.etree.iterparse() lugar, lo que evitará intentar construir un árbol DOM completo en la memoria y, de lo contrario, será mucho más eficiente que construyendo un árbol y luego iterando sobre él con XPath. (Piense en SAX, pero sin la API insana y dolorosa).

Implementar con iterparse podría verse así:

 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('''BlueChevy''')): print car 

… es más código, pero (si no estuviéramos usando StringIO para el ejemplo) podría procesar un archivo mucho más grande de lo que cabía en la memoria.

No sé si hay una mejor solución integrada dentro de lxml, pero puedes usar .get() :

 print parsedCarData[0].get('Model', '') 

Yo cogería la excepción:

 try: print parsedCarData[0]['Model'] except KeyError: print 'No model specified' 

Las excepciones en Python no son excepcionales en el mismo sentido que en otros idiomas, donde están más estrictamente vinculadas a las condiciones de error. En su lugar, frecuentemente son parte del uso normal de los módulos, por diseño. Un iterador genera StopIteration para indicar que ha llegado al final de la iteración, por ejemplo.

Edición: si está seguro de que solo este elemento puede estar vacío, @CharlesDuffy tiene razón en que usar get() es probablemente mejor. Pero en general consideraría el uso de excepciones para manejar fácilmente diversos resultados excepcionales.

La solución: use un bloque try/except para detectar el error clave.