4

我正在寻找一些关于如何完成的建议。我正在尝试仅使用 xpath 的解决方案:

一个html示例:

<div>
  <div>
    <div>text div (leaf)</div>
    <p>text paragraph (leaf)</p>
  </div>
</div>
<p>text paragraph 2 (leaf)</p>

代码:

doc = Nokogiri::HTML.fragment("- the html above -")
result = doc.xpath("*[not(child::*)]")


[#<Nokogiri::XML::Element:0x3febf50f9328 name="p" children=[#<Nokogiri::XML::Text:0x3febf519b718 "text paragraph 2 (leaf)">]>] 

但是这个 xpath 只给了我最后一个“p”。我想要的是一个扁平化的行为,只返回叶子节点。

以下是stackoverflow中的一些参考答案:

如何使用 XPath 表达式选择所有叶节点?

XPath - 获取没有特定类型子节点的节点

谢谢

4

3 回答 3

7

您可以使用以下命令查找所有没有子元素的元素节点:

//*[not(*)]

例子:

require 'nokogiri'

doc = Nokogiri::HTML.parse <<-end
<div>
  <div>
    <div>text div (leaf)</div>
    <p>text paragraph (leaf)</p>
  </div>
</div>
<p>text paragraph 2 (leaf)</p>
end

puts doc.xpath('//*[not(*)]').length
#=> 3

doc.xpath('//*[not(*)]').each do |e|
    puts e.text
end
#=> "text div (leaf)"
#=> "text paragraph (leaf)"
#=> "text paragraph 2 (leaf)"
于 2013-07-26T20:14:37.963 回答
3

您的代码的问题是以下语句:

doc = Nokogiri::HTML.fragment("- the html above -")

看这里:

require 'nokogiri'

html = <<END_OF_HTML
<div>
  <div>
    <div>text div (leaf)</div>
    <p>text paragraph (leaf)</p>
  </div>
</div>
<p>text paragraph 2 (leaf)</p>
END_OF_HTML


doc = Nokogiri::HTML(html)
#doc = Nokogiri::HTML.fragment(html)
results = doc.xpath("//*[not(child::*)]")
results.each {|result| puts result}

--output:--
<div>text div (leaf)</div>
<p>text paragraph (leaf)</p>
<p>text paragraph 2 (leaf)</p>

如果我运行这个:

doc = Nokogiri::HTML.fragment(html)
results = doc.xpath("//*[not(child::*)]")
results.each {|result| puts result}

我没有输出。

于 2013-07-26T20:16:35.420 回答
2

在 XPath 中,文本本身就是一个节点——因此,鉴于您的评论,您只想选择标签内容,而不是包含该内容的标签——但您会捕获一个<br/>(如果有的话)。

我猜你正在寻找所有不包含其他元素(标签)的元素(这不完全是你所要求的) - 那么你对@Justin Ko 的回答没问题并使用 XPath 表达式

//*[not(*)]

如果你真的想寻找所有的叶子节点,你不能使用*选择器,但需要使用node()

//node()[not(node())]

节点可以是元素,也可以是文本节点、注释、处理指令、属性甚至 XML 文档(但这些不能出现在其他元素中)。

如果您真的只想要文本节点,请//text()像@Priti 提议的那样选择,它确实在某种程度上准确地选择了您要求的节点(通过突出显示它们,而不是通过定义叶节点)。

于 2013-07-26T21:38:37.710 回答