我正在编写一个HTML解析器,它使用TagSoup将格式良好的结构传递给XMLSlurper。
这是通用代码:
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"
}
我希望每个人都允许我依次选择每个'li',这样我就可以检索相应的href和地址细节。相反,我得到了这个输出:
#href1#href2: Here is the addressTelephone number: telephoneHere is another addressAnother telephone: 0845 1111111
我已经检查了网络上的各种示例,这些示例要么处理XML,要么是“从此文件中检索所有链接”之类的单行示例。似乎it.h3.a. @ href表达式正在收集文本中的所有href,即使我传递了对父“li”节点的引用。
你可以告诉我吗:感谢。
答案 0 :(得分:11)
将grep替换为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"
}
然后你会得到
#href1: Here is the addressTelephone number: telephone
#href2: Here is another addressAnother telephone: 0845 1111111
grep返回一个ArrayList,但find返回一个NodeChild类:
println html.'**'.grep { it.@class == 'divclass' }.getClass()
println html.'**'.find { it.@class == 'divclass' }.getClass()
结果:
class java.util.ArrayList
class groovy.util.slurpersupport.NodeChild
因此,如果您想使用grep,那么您可以再嵌套另外一个这样的工作
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"
}
}
长话短说,在你的情况下,使用find而不是grep。
答案 1 :(得分:1)
这是一个棘手的问题。当只有一个元素有class =&#39; divclass&#39;以前的答案肯定没问题。如果grep有多个结果,那么单个结果的find()不是答案。指出结果是ArrayList是正确的。插入外部嵌套的.each()循环会在闭包参数 div 中提供GPathResult。从这里向下钻取可以继续预期的结果。
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"
}}
原始代码的行为也可以使用更多的解释。在Groovy中的List上访问属性时,您将获得一个新列表(大小相同),其中包含列表中每个元素的属性。 grep()找到的列表只有一个条目。然后我们得到一个属性 ol 的条目,这很好。接下来我们得到该条目的ol.it结果。它是一个size()== 1的列表,但是这次使用size()== 2的条目。如果我们想要的话,我们可以在那里应用外部循环并得到相同的结果:
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"
}}
在代表多个节点的任何GPathResult上,我们得到所有文本的串联。这是原始结果,首先是 @href ,然后是地址。
答案 2 :(得分:0)
我相信在撰写本文时,对于使用的版本,之前的答案都是正确的。但我使用HTTPBuilder 0.7.1和Grails 2.4.4与Groovy 2.3.7并且存在一个很大的问题 - HTML元素被转换为大写。看来这是由于NekoHTML在引擎盖下使用:
http://nekohtml.sourceforge.net/faq.html#uppercase
因此,接受答案中的解决方案必须写为:
html.'**'.find { it.@class == 'divclass' }.OL.LI.each { linkItem ->
def link = linkItem.H3.A.@href
def address = linkItem.ADDRESS.text()
println "$link: $address\n"
}
调试非常令人沮丧,希望它可以帮助某人。