Utilisation de XmlSlurper :Comment sélectionner des sous-éléments lors d'une itération sur un GPathResult

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

Question

J'écris un analyseur HTML qui utilise TagSoup pour transmettre une structure bien formée à XMLSlurper.

Voici le code généralisé :

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"
}

Je m'attendrais à ce que each me laisse sélectionner chaque « li » à son tour afin que je puisse récupérer les détails du href et de l'adresse correspondants.Au lieu de cela, j'obtiens ce résultat :

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

J'ai vérifié divers exemples sur le Web et ceux-ci traitent du XML ou sont des exemples d'une seule ligne comme "récupérer tous les liens de ce fichier".Il semble que l'expression it.h3.a.@href collecte tous les hrefs du texte, même si je lui transmets une référence au nœud parent 'li'.

Peux-tu me laisser savoir:

  • Pourquoi le résultat s'affiche-t-il
  • Comment puis-je récupérer les paires href/adresse pour chaque élément « li »

Merci.

Était-ce utile?

La solution

Remplacez grep par 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"
}

alors tu auras

#href1: Here is the addressTelephone number: telephone

#href2: Here is another addressAnother telephone: 0845 1111111

grep renvoie une ArrayList mais find renvoie une classe NodeChild :

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

résulte en:

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

donc si vous vouliez utiliser grep, vous pourriez alors en imbriquer un autre comme celui-ci pour que cela fonctionne

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"
    }
}

Pour faire court, dans votre cas, utilisez find plutôt que grep.

Autres conseils

C’était une question délicate.Lorsqu'il n'y a qu'un seul élément avec class='divclass', la réponse précédente convient certainement.S'il y avait plusieurs résultats de grep, alors un find() pour un seul résultat n'est pas la réponse.Soulignant que le résultat est une ArrayList est correct.L'insertion d'une boucle .each() externe imbriquée fournit un GPathResult dans le paramètre de fermeture div.À partir de là, l'exploration peut continuer avec le résultat attendu.

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"
}}

Le comportement du code d'origine peut également nécessiter un peu plus d'explications.Lorsqu'on accède à une propriété sur une liste dans Groovy, vous obtenez une nouvelle liste (même taille) avec la propriété de chaque élément de la liste.La liste trouvée par grep() ne contient qu'une seule entrée.Ensuite, nous obtenons une entrée pour la propriété vieux, ce qui est bien.Ensuite, nous obtenons le résultat de ol.it pour cette entrée.C'est encore une liste de size() == 1, mais cette fois avec une entrée de size() == 2.Nous pourrions y appliquer la boucle externe et obtenir le même résultat, si nous voulions :

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"
}}

Sur n'importe quel GPathResult représentant plusieurs nœuds, nous obtenons la concaténation de tout le texte.C'est le résultat original, d'abord pour @href, Puis pour adresse.

Je pense que les réponses précédentes sont toutes correctes au moment de la rédaction, pour la version utilisée.Mais j'utilise HTTPBuilder 0.7.1 et Grails 2.4.4 avec Groovy 2.3.7 et il y a un gros problème - Les éléments HTML sont transformés en majuscules. Il semble que cela soit dû à NekoHTML utilisé sous le capot :

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

Pour cette raison, la solution dans la réponse acceptée doit être écrite comme suit :

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

C'était très frustrant de déboguer, j'espère que cela aidera quelqu'un.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top