如何解析具有嵌套属性的HTML列表



我想解析一个生成的HTML文件,以获得顶部的<li>元素。HTML的结构如下:

<ul>
  <li><a href='#xyz'>toplevel1</a></li>
  <ul>
    <li><a href='#nested11'>nested11</a></li>
    <li><a href='#nested12'>nested12</a></li>
  </ul>
  <li><a href='#xxyyzdf'>toplevel2</a></li>
  <ul>
    <li><a href='#nested21'>nested21</a></li>
    <li><a href='#nested22'>nested22</a></li>
    <li><a href='#nested23'>nested23</a></li>
  </ul>
    ......
</ul> 

我试着使用regex和一点Nokogiri,因为我是Nokogiri。我没有找到一种方法来确定哪些链接是顶级链接。

页面结构如下:

<body>
  <div id="content" class="view">
    <div id="main-content" class="wiki-content group">
      <div>
        <ul>
          <li><a href='#YXZ-abd1'>toplevel1</a></li>
          <ul>
            <li><a href='#xyznested11'>nested11</a></li>
            <li><a href='#xyznested12'>nested12</a></li>
          </ul>
          <li><a href='#XZYG-SDF'>toplevel2</a></li>
          <ul>
            <li><a href='#nxyzested21'>nested21</a></li>
            <li><a href='#xyznested22'>nested22</a></li>
            <li><a href='#xyznested23'>nested23</a></li>
          </ul>
          ......
        </ul>
        ......

在我看来,这种结构非常奇怪,因为它是从合流中自动生成的HTML。

以下是我使用CSS选择器的方法:

require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<body>
  <div id="content" class="view">
    <div id="main-content" class="wiki-content group">
      <div>
        <ul>
          <li><a href='#YXZ-abd1'>toplevel1</a></li>
          <ul>
            <li><a href='#xyznested11'>nested11</a></li>
            <li><a href='#xyznested12'>nested12</a></li>
          </ul>
          <li><a href='#XZYG-SDF'>toplevel2</a></li>
          <ul>
            <li><a href='#nxyzested21'>nested21</a></li>
            <li><a href='#xyznested22'>nested22</a></li>
            <li><a href='#xyznested23'>nested23</a></li>
          </ul>
        </ul>
</div></div></div></body>
EOT
top_level = doc.search('div#main-content > div > ul')
puts top_level.to_html

捕获的:

# >> <ul>
# >> <li><a href="#YXZ-abd1">toplevel1</a></li>
# >>           <ul>
# >> <li><a href="#xyznested11">nested11</a></li>
# >>             <li><a href="#xyznested12">nested12</a></li>
# >>           </ul>
# >> <li><a href="#XZYG-SDF">toplevel2</a></li>
# >>           <ul>
# >> <li><a href="#nxyzested21">nested21</a></li>
# >>             <li><a href="#xyznested22">nested22</a></li>
# >>             <li><a href="#xyznested23">nested23</a></li>
# >>           </ul>
# >> </ul>

更加具体并找到下一级别的<li>标签:

top_level = doc.search('div#main-content > div > ul > li')
puts top_level.to_html
# >> <li><a href="#YXZ-abd1">toplevel1</a></li><li><a href="#XZYG-SDF">toplevel2</a></li>

抓取他们嵌入的<a>标签:

top_level = doc.search('div#main-content > div > ul > li > a')
puts top_level.to_html
# >> <a href="#YXZ-abd1">toplevel1</a><a href="#XZYG-SDF">toplevel2</a>

您应该看看Nokogiri的宝石。它将允许您使用xpath 解析html

require 'nokogiri'
doc = Nokogiri::HTML("<ul>...</ul>")
# Search for nodes by css
doc.css('a[href*=top]').each do |link|
  # do something here
end

为了使用CSS选择器只匹配顶级链接,我唯一能想到的就是将整个字符串临时包装在另一个易于选择的节点中,您将使用nokogiri解析整个字符串。

<myselectablenode>
  <ul>
    <li><a href='#top1'>toplevel1</a></li>
    <ul>
      <li><a href='#nested11'>nested11</a></li>
      <li><a href='#nested12'>nested12</a></li>
    </ul>
    <li><a href='#top2'>toplevel2</a></li>
    <ul>
      <li><a href='#nested21'>nested21</a></li>
      <li><a href='#nested22'>nested22</a></li>
      <li><a href='#nested23'>nested23</a></li>
    </ul>
  </ul>
</myselectablenode>

使用doc = Nokogiri::XML::Document.parse("<myselectablenode>" + mydoc + "</myselectablenode>")解析示例文档片段然后使用doc.css("myselectablenode > ul > li") 访问您的顶级元素

这里有一个更完整的例子:

$ cat > /tmp/scrape
  <ul>
    <li><a href='#top1'>toplevel1</a></li>
    <ul>
      <li><a href='#nested11'>nested11</a></li>
      <li><a href='#nested12'>nested12</a></li>
    </ul>
    <li><a href='#top2'>toplevel2</a></li>
    <ul>
      <li><a href='#nested21'>nested21</a></li>
      <li><a href='#nested22'>nested22</a></li>
      <li><a href='#nested23'>nested23</a></li>
    </ul>
  </ul>
^C
~$ irb
irb(main):001:0> contents = File.read("/tmp/scrape")
=> "  <ul>n    <li><a href='#top1'>toplevel1</a></li>n    <ul>n      <li><a href='#nested11'>nested11</a></li>n      <li><a href='#nested12'>nested12</a></li>n    </ul>n    <li><a href='#top2'>toplevel2</a></li>n    <ul>n      <li><a href='#nested21'>nested21</a></li>n      <li><a href='#nested22'>nested22</a></li>n      <li><a href='#nested23'>nested23</a></li>n    </ul>n  </ul>n"
irb(main):002:0> contents = "<myselectablenode>" + contents + "</myselectablenode>"
=> "<myselectablenode>  <ul>n    <li><a href='#top1'>toplevel1</a></li>n    <ul>n      <li><a href='#nested11'>nested11</a></li>n      <li><a href='#nested12'>nested12</a></li>n    </ul>n    <li><a href='#top2'>toplevel2</a></li>n    <ul>n      <li><a href='#nested21'>nested21</a></li>n      <li><a href='#nested22'>nested22</a></li>n      <li><a href='#nested23'>nested23</a></li>n    </ul>n  </ul>n</myselectablenode>"
irb(main):003:0> require "rubygems"; require "nokogiri"
=> true
irb(main):004:0> doc = Nokogiri::XML::Document.parse(contents)
=> #<Nokogiri::XML::Document:0x14031ec name="document" children=[#<Nokogiri::XML::Element:0x1402eb8 name="myselectablenode" children=[#<Nokogiri::XML::Text:0x1402c38 "  ">, #<Nokogiri::XML::Element:0x1402be8 name="ul" children=[#<Nokogiri::XML::Text:0x14028f0 "n    ">, #<Nokogiri::XML::Element:0x14028a0 name="li" children=[#<Nokogiri::XML::Element:0x14025a8 name="a" attributes=[#<Nokogiri::XML::Attr:0x14024cc name="href" value="#top1">] children=[#<Nokogiri::XML::Text:0x1402080 "toplevel1">]>]>, #<Nokogiri::XML::Text:0x1401e78 "n    ">, #<Nokogiri::XML::Element:0x1401e28 name="ul" children=[#<Nokogiri::XML::Text:0x1401b30 "n      ">, #<Nokogiri::XML::Element:0x1401ae0 name="li" children=[#<Nokogiri::XML::Element:0x14017e8 name="a" attributes=[#<Nokogiri::XML::Attr:0x140170c name="href" value="#nested11">] children=[#<Nokogiri::XML::Text:0x14012d4 "nested11">]>]>, #<Nokogiri::XML::Text:0x14010cc "n      ">, #<Nokogiri::XML::Element:0x140107c name="li" children=[#<Nokogiri::XML::Element:0x1400d84 name="a" attributes=[#<Nokogiri::XML::Attr:0x1400ca8 name="href" value="#nested12">] children=[#<Nokogiri::XML::Text:0x1400870 "nested12">]>]>, #<Nokogiri::XML::Text:0x1400668 "n    ">]>, #<Nokogiri::XML::Text:0x1400500 "n    ">, #<Nokogiri::XML::Element:0x14004b0 name="li" children=[#<Nokogiri::XML::Element:0x14001b8 name="a" attributes=[#<Nokogiri::XML::Attr:0x14000dc name="href" value="#top2">] children=[#<Nokogiri::XML::Text:0x13ffca4 "toplevel2">]>]>, #<Nokogiri::XML::Text:0x13ffa9c "n    ">, #<Nokogiri::XML::Element:0x13ffa4c name="ul" children=[#<Nokogiri::XML::Text:0x13ff754 "n      ">, #<Nokogiri::XML::Element:0x13ff704 name="li" children=[#<Nokogiri::XML::Element:0x13ff40c name="a" attributes=[#<Nokogiri::XML::Attr:0x13ff330 name="href" value="#nested21">] children=[#<Nokogiri::XML::Text:0x13feef8 "nested21">]>]>, #<Nokogiri::XML::Text:0x13fecf0 "n      ">, #<Nokogiri::XML::Element:0x13feca0 name="li" children=[#<Nokogiri::XML::Element:0x13fe9a8 name="a" attributes=[#<Nokogiri::XML::Attr:0x13fe8cc name="href" value="#nested22">] children=[#<Nokogiri::XML::Text:0x13fe494 "nested22">]>]>, #<Nokogiri::XML::Text:0x13fe28c "n      ">, #<Nokogiri::XML::Element:0x13fe23c name="li" children=[#<Nokogiri::XML::Element:0x13fdf44 name="a" attributes=[#<Nokogiri::XML::Attr:0x13fde68 name="href" value="#nested23">] children=[#<Nokogiri::XML::Text:0x13fda30 "nested23">]>]>, #<Nokogiri::XML::Text:0x13fd828 "n    ">]>, #<Nokogiri::XML::Text:0x13fd6c0 "n  ">]>, #<Nokogiri::XML::Text:0x13fd558 "n">]>]>
irb(main):005:0> doc.css("myselectablenode > ul > li")
=> [#<Nokogiri::XML::Element:0x14028a0 name="li" children=[#<Nokogiri::XML::Element:0x14025a8 name="a" attributes=[#<Nokogiri::XML::Attr:0x14024cc name="href" value="#top1">] children=[#<Nokogiri::XML::Text:0x1402080 "toplevel1">]>]>, #<Nokogiri::XML::Element:0x14004b0 name="li" children=[#<Nokogiri::XML::Element:0x14001b8 name="a" attributes=[#<Nokogiri::XML::Attr:0x14000dc name="href" value="#top2">] children=[#<Nokogiri::XML::Text:0x13ffca4 "toplevel2">]>]>]

如前所述,Nokogiri支持XPath查询。下面是一个XPath查询示例,用于获取其href包含短语"top"的<a>节点的集合:

require 'nokogiri'
doc = Nokogiri::HTML(example_html)
top_links = doc.xpath("//li/a[contains(@href, 'top')]")
top_links.size # => 2
top_links.map { |link| link.text } # => ["toplevel1", "toplevel2"]
top_links.map { |link| link.attr('href') } # => ["#top1", "#top2"]

XPath查询也提供了相当大的灵活性。在下面的例子中,我们想要抓取任何具有"special"类的行项目,但前提是它包含"top link":

html = <<-HTML
<ul>
  <li><a href='#top1'>toplevel1</a></li>
  <ul>
    <li><a href='#nested11'>nested11</a></li>
    <li><a href='#nested12'>nested12</a></li>
  </ul>
  <li><a href='#top2'>toplevel2</a></li>
  <ul>
    <li class='special'><a href='#nested21'>nested21</a></li>
    <li><a href='#nested22'>nested22</a></li>
    <li><a href='#nested23'>nested23</a></li>
  </ul>
  <li class='special'><a href='#top3'>toplevel3</a></li>
</ul>
HTML
doc = Nokogiri::HTML(html)
special_items_with_top_links = doc.xpath('//li[@class="special"]/a[contains(@href, "top")]/..')
puts special_items_with_top_links.size # => 1
special_items_with_top_links.each do |item|
  puts item.to_html 
  # => <li class="special"><a href="#top3">toplevel3</a></li>
  puts "#{item.attr('class')}: #{item.at('a').text}"
  # => special: toplevel3
end

相关内容

  • 没有找到相关文章

最新更新