Как выбрать кусок HTML с помощью селекторов Nokogiri и XPath или CSS? - PullRequest
2 голосов
/ 13 декабря 2011

В моем приложении Rails у меня есть HTML, подобный следующему, проанализированный в Nokogiri.

Я хочу иметь возможность выбирать фрагменты HTML.Например, как я могу выбрать блок HTML, который является частью <sup id="21">, используя XPath или CSS?Предположим, что в реальном 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>

Ответы [ 3 ]

1 голос
/ 13 декабря 2011

Похоже, вы хотите выбрать все между 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'])])

Или применение формулы пересечения множества Кайесса:

//sup[@id='21']|(//sup[@id='21']/following-sibling::node()[
    count(.|//sup[@id='23']/preceding-sibling::node())
     =
    count(//sup[@id='23']/preceding-sibling::node())])
1 голос
/ 14 декабря 2011

Вот простое решение, которое дает вам 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 голосов
/ 13 декабря 2011
require 'open-uri'
require 'nokogiri'

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