如何根据文档根目录的相对距离选择一组节点

时间:2014-10-26 12:36:03

标签: ruby xpath web-scraping nokogiri

我一直在考虑这个问题很长一段时间,但我似乎无法找到一个合理的解决方案:如何根据它们与文档根目录的相对距离来选择一组节点?

给出一个像Nokogiri一样解析的样本HTML片段:

<body>
  <nav>
    <ol>
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Contact</a></li>
    </ol>
  </nav>
  <ul>
    <li><a href="#">Feedback</a></li>
    <li><a href="#">Follow us</a></li>
  </ul>
</body>

假设我对此文档的唯一了解,那就是我要查找的节点是: 一组<a>个与<body>标记具有相同距离且匹配量最高的标记。在上面的示例中,这意味着匹配<ol>内的组,因为它具有与<body>具有相同距离的最大节点数。

关于如何解决这个问题的任何想法?

1 个答案:

答案 0 :(得分:0)

这是一个奇怪的需求/请求,但我从这开始:

require 'nokogiri'

doc = Nokogiri::HTML(<<EOT)
<body>
  <nav>
    <ol>
      <li><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Contact</a></li>
    </ol>
  </nav>
  <ul>
    <li><a href="#">Feedback</a></li>
    <li><a href="#">Follow us</a></li>
  </ul>
</body>
EOT

a_tags = doc.search('a')
            .group_by{ |n| n.path.split('/').count }
            .max_by{ |c, n| c }
            .last

a_tags
# => [#(Element:0x3fe3a6012a60 {
#       name = "a",
#       attributes = [ #(Attr:0x3fe3a6098750 { name = "href", value = "#" })],
#       children = [ #(Text "Home")]
#       }),
#     #(Element:0x3fe3a6012830 {
#       name = "a",
#       attributes = [ #(Attr:0x3fe3a6096914 { name = "href", value = "#" })],
#       children = [ #(Text "About")]
#       }),
#     #(Element:0x3fe3a601240c {
#       name = "a",
#       attributes = [ #(Attr:0x3fe3a6070ef8 { name = "href", value = "#" })],
#       children = [ #(Text "Contact")]
#       })]

a_tags中的最终结果是与根相等距离的节点数组。

从那以后我尝试了:

a_tags = doc.search('a').group_by{ |n| n.path.gsub(/\[\d+\]/, '') }.max_by{ |p, n| n.size }
# => ["/html/body/nav/ol/li/a",
#     [#(Element:0x3ff6f10d2c08 {
#        name = "a",
#        attributes = [ #(Attr:0x3ff6f10d1cf4 { name = "href", value = "#" })],
#        children = [ #(Text "Home")]
#        }),
#      #(Element:0x3ff6f10d2b18 {
#        name = "a",
#        attributes = [ #(Attr:0x3ff6f10cc254 { name = "href", value = "#" })],
#        children = [ #(Text "About")]
#        }),
#      #(Element:0x3ff6f10d2a8c {
#        name = "a",
#        attributes = [ #(Attr:0x3ff6f10c3b2c { name = "href", value = "#" })],
#        children = [ #(Text "Contact")]
#        })]]

返回一个由CSS路径(删除了索引值)和其下的节点列表组成的数组。我并不完全满意使用CSS路径来识别聚集的目标标签。

可以从节点向后走到文档的根目录,并将这些节点添加到数组中。然后,使用Array&#34; set&#34;您应该能够准确识别哪些节点位于相同的父节点下。我不确定如何处理<li>节点不同的事实。

希望那些应该给你一些想法。