用Nokogiri解析HTML并获取最接近的文本“ ”

问题描述 投票:1回答:3

我有HTML包含:

<div class = "s">
   <p> text1 </p>
   <div class = "i">
      <p> text2 </p>
   </div>
</div> 

我需要获得类<div>中最接近"s"的所有文本。

例如,我试图获取:

array = []
html.css("s").each do |element|
  array << element.text.strip
end

很好,除了在我的数组中显示"text2",而且我不想要这些。因此,对于"text2",最接近的<div>具有类"i",并且我不想在我的数组中看到它。

我该如何解决?可以有不同的类名和更深层的嵌套,例如:

<div class = "s">
   <p> text1 </p>
   <div class = "i">
      <p> text2 </p>
      <div class = "s">
         <p> text3 </p>
         <div class = "p">
           <p> text4 </p>
         </div>
      </div> 
   </div>
</div> 

由此,我想获得一个数组:["text1", "text3"]

html ruby nokogiri
3个回答
3
投票

这是一个更好的纯XPath答案。我的原始答案如下。

# Given a Nokogiri::HTML document in the `html` variable:
html.xpath("//text()[normalize-space() and ancestor::div[1][@class='s']]").map(&:text).map(&:strip)

这仅查找其最接近的div祖先具有s类的所有非空白文本节点。它与我的原始答案相同,只是它完全在XPath中完成。

<div class = "s">
   <p> text1 </p>
   <div class = "i">
      <p> text2 </p>
   </div>
</div>
# => ["text1"]

<div class = "s">
   <p> text1 </p>
   <div class = "i">
      <p> text2 </p>
      <div class = "s">
         <p> text3 </p>
         <div class = "p">
           <p> text4 </p>
         </div>
      </div>
   </div>
</div>
# => ["text1", "text3"]

<div class = "s">
  <div class='p'>
    text 1
  </div>
  text 2
</div>
# => ["text 2"]

原始答案:

html.search("//div[@class='s']//text()").
  select {|t| t.ancestors("div").first.attr("class") == "s" }.
  map(&:text).join.squeeze.strip
# => "text1"

这里的基本思想是,我们找到所有从div.s降序的文本节点,然后为每个文本节点找到最接近的div祖先,并且仅接受具有最接近div祖先且类别为s的节点。 。

这有点占用CPU,但是满足严格的要求。


1
投票

您可以做:

html.css('.s > p').map {|node| node.text.strip }

0
投票

我将从这里开始:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT, &:noblanks)
<div class = "s">
   <p> text1 </p>
   <div class = "i">
      <p> text2 </p>
      <div class = "s">
         <p> text3 </p>
         <div class = "p">
           <p> text4 </p>
         </div>
      </div> 
   </div>
</div> 
EOT

doc.search('.s').map{ |div| div.child.text.strip } 
# => ["text1", "text3"]

我认为,由于HTML格式的原因,很难找到合适的节点,即child节点的".s",是下一个包含“ \ n”的Text节点。忽略它们很困难,因为它们可能不是文本,可能是您要返回的节点。

诀窍是告诉Nokogiri在解析文档时剥离空白节点,这实际上将使HTML变平,删除所有缩进,从而可以相信目标之后的下一个节点就是想要的节点。


foobar会导致此技术失败。

是的,它将,并且将需要其他逻辑来清除那些:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT, &:noblanks)
<div class = "s">
   <p> text1 </p>
   <div class = "i">
      <p> text2 </p>
      <div class = "s">
         <p> text3 </p>
         <div class = "p">
           <p> text4 </p>
         </div>
      </div> 
      <div class='s'><div class='i'>foobar</div></div>
   </div>
</div> 
EOT

这是旧的逻辑:

doc.search('.s').map{ |div| div.child.text.strip } 
# => ["text1", "text3", "foobar"]

以及快速测试以清除不需要的内容:

doc.search('.s').reject{ |div| div.child['class'] == 'i' }.map{ |div| div.child.text.strip } 
# => ["text1", "text3"]
© www.soinside.com 2019 - 2024. All rights reserved.