2009-02-28 10 views
14

Estoy tratando de analizar algo de HTML con XPath. Siguiendo el ejemplo de XML simplificado a continuación, deseo hacer coincidir la cadena 'Texto 1', luego tomar el contenido del nodo relevante content.Cómo hacer coincidir un nodo de texto y luego seguir los nodos principales usando XPath

<doc> 
    <block> 
     <title>Text 1</title> 
     <content>Stuff I want</content> 
    </block> 

    <block> 
     <title>Text 2</title> 
     <content>Stuff I don't want</content> 
    </block> 
</doc> 

código Mi Python lanza una tambaleante:

>>> from lxml import etree 
>>> 
>>> tree = etree.XML("<doc><block><title>Text 1</title><content>Stuff 
I want</content></block><block><title>Text 2</title><content>Stuff I d 
on't want</content></block></doc>") 
>>> 
>>> # get all titles 
... tree.xpath('//title/text()') 
['Text 1', 'Text 2'] 
>>> 
>>> # match 'Text 1' 
... tree.xpath('//title/text()="Text 1"') 
True 
>>> 
>>> # Follow parent from selected nodes 
... tree.xpath('//title/text()/../..//text()') 
['Text 1', 'Stuff I want', 'Text 2', "Stuff I don't want"] 
>>> 
>>> # Follow parent from selected node 
... tree.xpath('//title/text()="Text 1"/../..//text()') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "lxml.etree.pyx", line 1330, in lxml.etree._Element.xpath (src/ 
lxml/lxml.etree.c:14542) 
    File "xpath.pxi", line 287, in lxml.etree.XPathElementEvaluator.__ca 
ll__ (src/lxml/lxml.etree.c:90093) 
    File "xpath.pxi", line 209, in lxml.etree._XPathEvaluatorBase._handl 
e_result (src/lxml/lxml.etree.c:89446) 
    File "xpath.pxi", line 194, in lxml.etree._XPathEvaluatorBase._raise 
_eval_error (src/lxml/lxml.etree.c:89281) 
lxml.etree.XPathEvalError: Invalid type 

Es esto posible en XPath? ¿Debo expresar lo que quiero hacer de otra manera?

Respuesta

22

¿Lo quieres?

//title[text()='Text 1']/../content/text() 
+0

Duh, muy simple! Tiene sentido que estoy seleccionando el atributo text() ahora. – Mat

+2

también puede usar // block [title = 'Text 1']/content para obtener el nodo de contenido relevante – Dror

+0

@Dror: Ahora que es útil saber. – Mat

16

Uso:

string(/*/*/title[. = 'Text 1']/following-sibling::content) 

Esto representa al menos dos mejoras en comparación con la solución actualmente aceptado de Johannes Weiß:

  1. La abreviatura muy caro " // " (por lo general causa el quién El documento XML que se escaneará) se evita como debería ser siempre que la estructura del documento XML se conozca de antemano.

  2. No hay vuelta atrás al padre (el paso de localización "/ .." se evita)

+0

Mejora justa, mi documento real es HTML y la parte del 'título' está anidada en cinco niveles profundos, así que tengo que volver unos cinco padres para llegar al área de 'contenido'. Tendré en cuenta el primer punto, aunque no supondrá una gran diferencia para un truco sucio. – Mat

+2

¿Qué hace '/ */* /' hacer? Lo intento en un documento bastante grande y parece tan lento como '//'. – dentarg

+2

@dentarg: '/ */*' selecciona todos los elementos que son elementos secundarios del elemento superior del documento. Es mucho más rápido que '// someName' que atraviesa el documento completo y selecciona cada elemento llamado' "someName" '. En esta respuesta podemos usar una expresión aún más eficiente: 'string (/ */*/title [. = 'Text 1'] [1]/following-sibling :: content)' La expresión en la respuesta no debe ser menos eficiente, dado un procesador XPath que optimiza bien, porque siempre que se proporcione a la función 'string()' un argumento que es un conjunto de nodos, solo produce el valor de cadena del primer nodo de este conjunto de nodos. –

Cuestiones relacionadas