2010-11-07 27 views
16
<span class='python'> 
    <a>google</a> 
    <a>chrome</a> 
</span> 

Quiero obtener chrome y lo tengo funcionando así.Obtener el segundo elemento de texto con XPath?

q = item.findall('.//span[@class="python"]//a') 
t = q[1].text # first element = 0 

Me gustaría combinarlo en una sola expresión XPath y solo obtener un elemento en lugar de una lista.
Intenté esto pero no funciona.

t = item.findtext('.//span[@class="python"]//a[2]') # first element = 1 

Y el HTML actual, no simplificado, es así.

<span class='python'> 
    <span> 
    <span> 
     <img></img> 
     <a>google</a> 
    </span> 
    <a>chrome</a> 
    </span> 
</span> 
+2

Su expresión './/span[@class="python "] // a [2]' funciona para mí. –

+0

Hmmm parece que tengo un error en alguna parte, o la simplificación del HTML real que publiqué es demasiado fácil. Lo intentaré y luego modificaré la pregunta. –

+0

@pdnsk: Buena pregunta, +1. Ver mi respuesta para una explicación y una solución simple. :) –

Respuesta

31

He intentado esto pero no funciona.

t = item.findtext('.//span[@class="python"]//a[2]') 

Este es un FAQ de la abreviatura //.

.//a[2] significa: Seleccione todos los a descendientes del nodo actual que son el segundo hijo a de sus padres. Por lo tanto, esto puede seleccionar más de un elemento o ningún elemento, según el documento XML concreto.

En otras palabras, el operador [] tiene una precedencia mayor que //.

Si desea sólo uno (el segundo) de todos los nodos devueltos usted tiene que utilizar paréntesis para forzar a su precedencia deseada:

(.//a)[2]

Esto realmente selecciona la segunda a descendiente del nodo actual.

Para la expresión real utilizado en la pregunta, cambiarlo a:

(.//span[@class="python"]//a)[2] 

o cambiarlo a:

(.//span[@class="python"]//a)[2]/text() 
+0

Gracias por la explicación, pero tengo una pregunta, o en realidad dos. Si solo hay un elemento coincidente, '' [2] 'arrojará una excepción o' '¿Ninguno? ¿Y sabes por qué esto funciona con 'xpath' pero no con' findtext'? –

+1

@pdnsk: Mi respuesta es XPath puro. No sé Python. –

+0

Lo intenté y simplemente no devuelve ningún elemento, lo cual es bueno porque una de las razones por las que quería evitar las listas y tenerlo en una sola expresión es no tener una verificación adicional. –

2

No estoy seguro de cuál es el problema ...

>>> d = """<span class='python'> 
... <a>google</a> 
... <a>chrome</a> 
... </span>""" 
>>> from lxml import etree 
>>> d = etree.HTML(d) 
>>> d.xpath('.//span[@class="python"]/a[2]/text()') 
['chrome'] 
>>> 
+0

Gracias, esto funciona. Parece ser un problema de 'encontrar texto'. –

2

de:

o la simplificación de los HTML real que he publicado es demasiado simple

Tienes razón. ¿Cuál es el significado de .//span[@class="python"]//a[2]? Esto se extenderá a:

self::node() 
/descendant-or-self::node() 
    /child::span[attribute::class="python"] 
    /descendant-or-self::node() 
    /child::a[position()=2] 

Se finaly seleccionará el segundo a niño (fn:position() se refiere a la child hacha). Por lo tanto, nada se seleccione si el documento es como:

<span class='python'> 
    <span> 
    <span> 
     <img></img> 
     <a>google</a><!-- This is the first "a" child of its parent --> 
    </span> 
    <a>chrome</a><!-- This is also the first "a" child of its parent --> 
    </span> 
</span> 

Si desea que el segundo de todos los descendientes, utilice:

descendant::span[@class="python"]/descendant::a[2] 
+0

Gracias, he actualizado la pregunta. –

+0

Funciona con 'xpath' pero no con' findtext', y devuelve una lista con un elemento. –

+0

@pdknsk: Esto se debe a que esta expresión XPath devuelve un resultado de conjunto de nodos: podría estar vacío, podría ser un singleton, podría tener muchos tramos con una clase "python" y un segundo descendiente ... Si desea el ** valor de cadena ** del primero de estos resultados, use la función 'cadena()' con esta expresión como argumento. No sé qué tipo de tipo de datos puede devolver su método 'xpath' ... –

Cuestiones relacionadas