我目前正在使用 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-tags。我希望选择所有带框的 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 中按组选择这些内部段落并将它们组合成一个字符串?
如果您不介意使用 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 迭代段落并制定字符串。
可以用 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