【问题标题】:How do I select a chunk of HTML using Nokogiri and XPath or CSS selectors?如何使用 Nokogiri 和 XPath 或 CSS 选择器选择一块 HTML?
【发布时间】:2011-12-13 13:34:52
【问题描述】:

在我的 Rails 应用程序中,我有如下 HTML,在 Nokogiri 中解析。

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

我想通过<sup id=*> 拆分 HTML,但问题是节点是兄弟节点。

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

【问题讨论】:

    标签: ruby-on-rails ruby xpath nokogiri


    【解决方案1】:

    这是一个简单的解决方案,它为您提供NodeSets 以及&lt;sup … class="v"&gt; 之间的所有节点,由它们的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']
    

    【讨论】:

    • @DavidWest 没错,最后的“更通用”代码是“重新打开”Nokogiri 类并向其添加新的实例方法,即“monkeypatching”。
    【解决方案2】:

    您似乎想要选择 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())])
    

    【讨论】:

      【解决方案3】:
      require 'open-uri'
      require 'nokogiri'
      
      doc = Nokogiri::HTML(open("http://www.yoururl"))
      doc.xpath('//sup[id="21"]').each do |node|
        puts node.text
      end
      

      【讨论】:

      • 投反对票是因为 a)您的 XPath 无效,并且 b)这并不能解决 OP 要求的问题(选择直到下一个类似元素)。
      猜你喜欢
      • 1970-01-01
      • 2011-02-06
      • 2013-11-17
      • 2020-04-23
      • 2019-10-11
      • 2023-01-29
      • 1970-01-01
      • 2014-09-03
      • 1970-01-01
      相关资源
      最近更新 更多