在Ruby中使用xPath选择组中的段落

时间:2012-11-14 19:45:51

标签: ruby xpath screen-scraping web-scraping nokogiri

我目前正在使用Ruby和xPath开展一个小型网络抓取项目。不幸的是,该网站的结构非常糟糕,这让我陷入了一个小小的问题:

<h3>Relevant Headline</h3>
<p class="class_a class_b">Content starts in this paragraph...</p>
<p class="class_a ">...but this content belongs to the preceding paragraph</p>
<p class="class_a class_b">Content starts in this paragraph...</p>
<p class="class_a ">...but this content belongs to the preceding paragraph</p>
<h3>Some other Headline</h3>

正如您所看到的,有2个h3-Tags可以构建多个p-tag。我想要选择所有带框的p标签。我发现已经有以下xPath来做到这一点:

h3[contains(text(),"Relevant")]/following-sibling::p[1 = count(preceding-sibling::h3[1] | ../h3[contains(text(),"Relevant")])]

但现在遇到困难:上面两段中的两段属于一起。带有class_b(第一个)的段落开始一个新的数据条目,下一个(第二个)属于这个条目。 3和4是一样的。问题是:有时3段属于一起,有时4段,但大多数时候有一对段落属于一起。

如何按组选择这些内部段落并将它们合并到Ruby中的一个字符串?

2 个答案:

答案 0 :(得分:4)

如果你不介意使用xpath和nokogiri的组合,你可以这样做:

paragraph_text = Array.new
doc.xpath('//p[preceding-sibling::h3[1][contains(text(), "Relevant")]]').each do |p|
    if p.attribute('class').text.include?('class_b')
        paragraph_text << p.content
    else
        paragraph_text[-1] += p.text
    end
end
puts paragraph_text
#=> ["Content starts in this paragraph......but this content belongs to the preceding paragraph",  "Content starts in this paragraph......but this content belongs to the preceding paragraph"]

基本上xpath用于获取段落标记。然后,使用nokogiri / ruby​​,遍历段落并制定字符串。

答案 1 :(得分:3)

可以使用xpath完成,但我认为使用slice_before将它们分组更容易:

doc.search('*').slice_before{|n| n.name == 'h3'}.each do |h3_group|
  h3_group.slice_before{|n| n[:class] && n[:class]['class_b']}.to_a[1..-1].each do |p_group|
    puts p_group.map(&:text) * ' '
  end
end

<强>更新

使用css的另一种选择:

doc.search('p.class_b').each do |p|
  str, next_node = p.text, p
  while next_node = next_node.at('+ p:not([class*=class_b])')
    str += " #{next_node.text}"
  end
  puts str
end