如何使用 Rails 和 Nokogiri 查找直接子项而不是嵌套子项?
How do I find direct children and not nested children using Rails and Nokogiri?
我正在使用 Rails 4.2.7 与 Ruby (2.3) 和 Nokogiri。我如何找到 table 的最直接的 tr 子级而不是嵌套子级?目前我在 table 中找到 table 行,就像这样……
tables = doc.css('table')
tables.each do |table|
rows = table.css('tr')
这不仅可以找到 table 的直接行,例如
<table>
<tbody>
<tr>…</tr>
但它也在行中查找行,例如
<table>
<tbody>
<tr>
<td>
<table>
<tr>This is found</tr>
</table>
</td>
</tr>
如何优化搜索以仅查找直接 tr 元素?
不知道直接用css/xpath能不能实现,所以写了一个递归查找节点的小方法。一找到就停止递归。
xml= %q{
<root>
<table>
<tbody>
<tr nested="false">
<td>
<table>
<tr nested="true">
This is found</tr>
</table>
</td>
</tr>
</tbody>
</table>
<another_table>
<tr nested = "false">
<tr nested = "true">
</tr>
</another_table>
<tr nested = "false"/>
</root>
}
require 'nokogiri'
doc = Nokogiri::XML.parse(xml)
class Nokogiri::XML::Node
def first_children_found(desired_node)
if name == desired_node
[self]
else
element_children.map{|child|
child.first_children_found(desired_node)
}.flatten
end
end
end
doc.first_children_found('tr').each do |tr|
puts tr["nested"]
end
#=>
# false
# false
# false
您可以使用 XPath 分几步完成。首先,您需要找到 table
的“级别”(即它在其他 table 中的嵌套程度),然后找到具有相同数量 [=13] 的所有后代 tr
=]祖先:
tables = doc.xpath('//table')
tables.each do |table|
level = table.xpath('count(ancestor-or-self::table)')
rows = table.xpath(".//tr[count(ancestor::table) = #{level}]")
# do what you want with rows...
end
在更一般的情况下,您可能 tr
直接嵌套在其他 tr
中,您可以这样做(这将是无效的 HTML,但您可能有 XML 或其他一些标签):
tables.each do |table|
# Find the first descendant tr, and determine its level. This
# will be a "top-level" tr for this table. "level" here means how
# many tr elements (including itself) are between it and the
# document root.
level = table.xpath("count(descendant::tr[1]/ancestor-or-self::tr)")
# Now find all descendant trs that have that same level. Since
# the table itself is at a fixed level, this means all these nodes
# will be "top-level" rows for this table.
rows = table.xpath(".//tr[count(ancestor-or-self::tr) = #{level}]")
# handle rows...
end
第一步可以分解成两个单独的查询,这样可能更清楚:
first_tr = table.at_xpath(".//tr")
level = first_tr.xpath("count(ancestor-or-self::tr)")
(如果 table 没有 tr
,这将失败,因为 first_tr
将是 nil
。上面的组合 XPath 正确处理了这种情况.)
我正在使用 Rails 4.2.7 与 Ruby (2.3) 和 Nokogiri。我如何找到 table 的最直接的 tr 子级而不是嵌套子级?目前我在 table 中找到 table 行,就像这样……
tables = doc.css('table')
tables.each do |table|
rows = table.css('tr')
这不仅可以找到 table 的直接行,例如
<table>
<tbody>
<tr>…</tr>
但它也在行中查找行,例如
<table>
<tbody>
<tr>
<td>
<table>
<tr>This is found</tr>
</table>
</td>
</tr>
如何优化搜索以仅查找直接 tr 元素?
不知道直接用css/xpath能不能实现,所以写了一个递归查找节点的小方法。一找到就停止递归。
xml= %q{
<root>
<table>
<tbody>
<tr nested="false">
<td>
<table>
<tr nested="true">
This is found</tr>
</table>
</td>
</tr>
</tbody>
</table>
<another_table>
<tr nested = "false">
<tr nested = "true">
</tr>
</another_table>
<tr nested = "false"/>
</root>
}
require 'nokogiri'
doc = Nokogiri::XML.parse(xml)
class Nokogiri::XML::Node
def first_children_found(desired_node)
if name == desired_node
[self]
else
element_children.map{|child|
child.first_children_found(desired_node)
}.flatten
end
end
end
doc.first_children_found('tr').each do |tr|
puts tr["nested"]
end
#=>
# false
# false
# false
您可以使用 XPath 分几步完成。首先,您需要找到 table
的“级别”(即它在其他 table 中的嵌套程度),然后找到具有相同数量 [=13] 的所有后代 tr
=]祖先:
tables = doc.xpath('//table')
tables.each do |table|
level = table.xpath('count(ancestor-or-self::table)')
rows = table.xpath(".//tr[count(ancestor::table) = #{level}]")
# do what you want with rows...
end
在更一般的情况下,您可能 tr
直接嵌套在其他 tr
中,您可以这样做(这将是无效的 HTML,但您可能有 XML 或其他一些标签):
tables.each do |table|
# Find the first descendant tr, and determine its level. This
# will be a "top-level" tr for this table. "level" here means how
# many tr elements (including itself) are between it and the
# document root.
level = table.xpath("count(descendant::tr[1]/ancestor-or-self::tr)")
# Now find all descendant trs that have that same level. Since
# the table itself is at a fixed level, this means all these nodes
# will be "top-level" rows for this table.
rows = table.xpath(".//tr[count(ancestor-or-self::tr) = #{level}]")
# handle rows...
end
第一步可以分解成两个单独的查询,这样可能更清楚:
first_tr = table.at_xpath(".//tr")
level = first_tr.xpath("count(ancestor-or-self::tr)")
(如果 table 没有 tr
,这将失败,因为 first_tr
将是 nil
。上面的组合 XPath 正确处理了这种情况.)