Usando XmlSlurper: ¿Cómo seleccionar sub-elementos, mientras que la iteración en una GPathResult

StackOverflow https://stackoverflow.com/questions/1675542

Pregunta

Estoy escribiendo un analizador de HTML, que utiliza TagSoup para pasar una estructura bien formada a XMLSlurper.

Aquí está el código generalizada:

def htmlText = """
<html>
<body>
<div id="divId" class="divclass">
<h2>Heading 2</h2>
<ol>
<li><h3><a class="box" href="#href1">href1 link text</a> <span>extra stuff</span></h3><address>Here is the address<span>Telephone number: <strong>telephone</strong></span></address></li>
<li><h3><a class="box" href="#href2">href2 link text</a> <span>extra stuff</span></h3><address>Here is another address<span>Another telephone: <strong>0845 1111111</strong></span></address></li>
</ol>
</div>
</body>
</html>
"""     

def html = new XmlSlurper(new org.ccil.cowan.tagsoup.Parser()).parseText( htmlText );

html.'**'.grep { it.@class == 'divclass' }.ol.li.each { linkItem ->
    def link = linkItem.h3.a.@href
    def address = linkItem.address.text()
    println "$link: $address\n"
}

Yo esperaría que el cada uno para que vamos a seleccionar cada uno 'li' a su vez para que pueda recuperar los datos correspondientes a href y dirección. En cambio, yo estoy haciendo esta salida:

#href1#href2: Here is the addressTelephone number: telephoneHere is another addressAnother telephone: 0845 1111111

He comprobado varias ejemplo en la web y estos tampoco trato con XML, o son ejemplos de una sola línea como "recuperar todos los enlaces de este archivo". Es parece que la expresión it.h3.a. @ href está recogiendo todos hrefs en el texto, a pesar de que estoy pasando una referencia al nodo 'li' padres.

¿Puede usted, hágamelo saber:

  • ¿Por qué estoy consiguiendo el resultado que se muestra
  • ¿Cómo puedo recuperar los pares href / dirección para cada elemento 'li'

Gracias.

¿Fue útil?

Solución

Reemplazar grep con find:

html.'**'.find { it.@class == 'divclass' }.ol.li.each { linkItem ->
    def link = linkItem.h3.a.@href
    def address = linkItem.address.text()
    println "$link: $address\n"
}

entonces obtendrá

#href1: Here is the addressTelephone number: telephone

#href2: Here is another addressAnother telephone: 0845 1111111

grep devuelve un ArrayList, pero vuelve a encontrar una clase NodeChild:

println html.'**'.grep { it.@class == 'divclass' }.getClass()
println html.'**'.find { it.@class == 'divclass' }.getClass()

resultados en:

class java.util.ArrayList
class groovy.util.slurpersupport.NodeChild

Así, si desea utilizar grep A continuación, podría nido otra cada uno como este para que funcione

html.'**'.grep { it.@class == 'divclass' }.ol.li.each {
    it.each { linkItem ->
        def link = linkItem.h3.a.@href
        def address = linkItem.address.text()
        println "$link: $address\n"
    }
}

Para resumir, en su caso, utilizar en lugar de encontrar grep.

Otros consejos

Esta era es un asunto difícil. Cuando no es sólo un elemento con 'divclass' class = la respuesta anterior es asegurarse bien. Si hubo múltiples resultados de grep, entonces un hallazgo () para un solo resultado no es la respuesta. Señalando que el resultado es un ArrayList es correcta. Inserción de un bucle .Cada exterior encajado () proporciona una GPathResult en el parámetro de cierre div . A partir de aquí el taladro abajo puede continuar con el resultado esperado.

html."**".grep { it.@class == 'divclass' }.each { div -> div.ol.li.each { linkItem ->
   def link = linkItem.h3.a.@href
   def address = linkItem.address.text()
   println "$link: $address\n"
}}

El comportamiento del código original se puede utilizar un poco más de una explicación también. Cuando se accede a una propiedad en una lista en Groovy, obtendrá una nueva lista (del mismo tamaño) con la propiedad de cada elemento de la lista. La lista que se encuentra por grep () tiene sólo una entrada. Entonces conseguimos una entrada para la propiedad ol , que está bien. A continuación se obtiene el resultado de ol.it para esa entrada. Es una lista de tamaño () == 1 de nuevo, pero esta vez con una entrada de tamaño () == 2. Se podría aplicar el bucle externo allí y obtener el mismo resultado, si queríamos:

html."**".grep { it.@class == 'divclass' }.ol.li.each { it.each { linkItem ->
   def link = linkItem.h3.a.@href
   def address = linkItem.address
   println "$link: $address\n"
}}

En cualquier GPathResult que representa múltiples nodos, se obtiene la concatenación de todo el texto. Ese es el resultado original, primero por @href , entonces Dirección .

Creo que las respuestas anteriores son los correctos en el momento de la escritura, para la versión utilizada. Pero yo estoy usando HTTPBuilder 0.7.1 y 2.4.4 Grails con Groovy 2.3.7 y hay un gran problema - elementos HTML se transforman en mayúsculas Parece que esto es debido a NekoHTML utiliza bajo el capó. :

http://nekohtml.sourceforge.net/faq.html#uppercase

Debido a esto, la solución en la respuesta aceptada debe ser escrito como:

html.'**'.find { it.@class == 'divclass' }.OL.LI.each { linkItem ->
    def link = linkItem.H3.A.@href
    def address = linkItem.ADDRESS.text()
    println "$link: $address\n"
}

Esto fue muy frustrante para depurar, espero que ayude a alguien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top