2011-12-13 60 views
2

在我的Rails应用程序中,我有如下的HTML,在Nokogiri中解析。如何使用Nokogiri和XPath或CSS选择器来选择一大块HTML?

我希望能够选择HTML的块。例如,如何使用XPath或CSS选择属于<sup id="21">的HTML块?假设在真正的HTML中,********的部分不存在。

我想分割HTML <sup id=*>但问题是节点是兄弟姐妹。

<sup class="v" id="20"> 
1 
</sup> 
this is some random text 
<p></p>    
more random text 
<sup class="footnote" value='fn1'> 
[v] 
</sup> 

# ****************************** starting here 
<sup class="v" id="21"> 
2 
</sup> 
now this is a different section 
<p></p>    
how do we keep this separate 
<sup class="footnote" value='fn2'> 
[x] 
</sup> 
# ****************************** ending here 

<sup class="v" id="23"> 
3 
</sup> 
this is yet another different section 
<p></p>    
how do we keep this separate too 
<sup class="footnote" value='fn3'> 
[r] 
</sup> 

回答

1

这里有一个简单的解决方案,让您NodeSet s的所有<sup … class="v">之间的节点,通过他们的id散列。

doc = Nokogiri.HTML(your_html) 

nodes_by_vsup_id = Hash.new{ |k,v| k[v]=Nokogiri::XML::NodeSet.new(doc) } 
last_id = nil 
doc.at('body').children.each do |n| 
    last_id = n['id'] if n['class']=='v' 
    nodes_by_vsup_id[last_id] << n 
end 

puts nodes_by_vsup_id['21'] 
#=> <sup class="v" id="21"> 
#=> 2 
#=> </sup> 
#=> 
#=> now this is a different section 
#=> <p></p> 
#=>  
#=> how do we keep this separate 
#=> <sup class="footnote" value="fn2"> 
#=> [x] 
#=> </sup> 

或者,如果你真的不想要的分界“SUP”是集合的一部分,而不是做:

doc.at('body').elements.each do |n| 
    if n['class']=='v' 
    last_id = n['id'] 
    else 
    nodes_by_vsup_id[last_id] << n 
    end 
end 

这里有一个替代方案,偶更通用的解决方案:

class Nokogiri::XML::NodeSet 
    # Yields each node in the set to your block 
    # Returns a hash keyed by whatever your block returns 
    # Any nodes that return nil/false are grouped with the previous valid value 
    def group_chunks 
    Hash.new{ |k,v| k[v] = self.class.new(document) }.tap do |result| 
     key = nil 
     each{ |n| result[key = yield(n) || key] << n } 
    end 
    end 
end 

root_items = doc.at('body').children 
separated = root_items.group_chunks{ |node| node['class']=='v' && node['id'] } 
puts separated['21'] 
+0

你在那里哼哼Nokogiri,对不对? –

+0

@DavidWest这是正确的,最后的“甚至更通用”的代码是“重新打开”Nokogiri类,并添加一个新的实例方法,即“monkeypatching”。 – Phrogz

-1
require 'open-uri' 
require 'nokogiri' 

doc = Nokogiri::HTML(open("http://www.yoururl")) 
doc.xpath('//sup[id="21"]').each do |node| 
    puts node.text 
end 
+0

由于a)您的XPath无效,并且b)这不能解决OP所要求的问题(直到下一个类似元素为止)。 – Phrogz

1

看起来你要选择的用sup@id='21'sup@id='23'之间的一切。使用下面的即席表达:

//sup[@id='21']|(//sup[@id='21']/following-sibling::node()[ 
    not(self::sup[@id='23'] or preceding-sibling::sup[@id='23'])]) 

还是Kayessian节点集合相交公式的应用:

//sup[@id='21']|(//sup[@id='21']/following-sibling::node()[ 
    count(.|//sup[@id='23']/preceding-sibling::node()) 
    = 
    count(//sup[@id='23']/preceding-sibling::node())])