【问题标题】:Scraping multiple table row siblings with Nokogiri使用 Nokogiri 抓取多个表行兄弟
【发布时间】:2015-05-17 08:33:14
【问题描述】:

我正在尝试使用以下标记解析表格。

<table>
  <tr class="athlete">
    <td colspan="2" class="name">Alex</td>
  </tr>
  <tr class="run">
    <td>5.00</td>
    <td>10.00</td>
  </tr>
  <tr class="run">
    <td>5.20</td>
    <td>10.50</td>
  </tr>
  <tr class="end"></tr>
  <tr class="athlete">
    <td colspan="2" class="name">John</td>
  </tr>
  <tr class="run">
    <td>5.00</td>
    <td>10.00</td>
  </tr>
  <tr class="end"></tr>
</table>

我需要遍历每个 .athlete 表行并获取下面的每个同级 .run 表行,直到到达 .end 行。然后为下一位运动员重复,依此类推。一些 .athlete 行有两个 .run 行,另一些有一个。

这是我目前所拥有的。我遍历运动员:

require 'rubygems'
require 'nokogiri'
require 'open-uri'

url = "http://myurl.com"
doc = Nokogiri::HTML(open(url))

doc.css(".athlete").each do |athlete|
  puts athlete.at_css("name").text
  # Loop through the sibling .run rows until I reach the .end row
  # output the value of the td’s in the .run row
end

我不知道如何获取每个同级的 .run 行,并在 .end 行处停止。我觉得如果表格更好地形成会更容易,但不幸的是我无法控制标记。任何帮助将不胜感激!

【问题讨论】:

    标签: ruby nokogiri


    【解决方案1】:

    require 'nokogiri'
    
    doc = <<DOC
    <table>
      <tr class="athlete">
        <td colspan="2" class="name">Alex</td>
      </tr>
      <tr class="run">
        <td>5.00</td>
        <td>10.00</td>
      </tr>
      <tr class="run">
        <td>5.20</td>
        <td>10.50</td>
      </tr>
      <tr class="end"></tr>
      <tr class="athlete">
        <td colspan="2" class="name">John</td>
      </tr>
      <tr class="run">
        <td>5.00</td>
        <td>10.00</td>
      </tr>
      <tr class="end"></tr>
    </table>
    DOC
    
    doc = Nokogiri::HTML(doc)
    # You can exclude .end, if it is always empty? and not required
    trs = doc.css('.athlete, .run, .end').to_a
    # This will return [['athlete', 'run', ...,'end'], ['athlete', 'run', ...,'end'] ...]
    athletes = trs.slice_before{ |elm| elm.attr('class') =='athlete' }.to_a
    
    athletes.map! do |athlete|
        {
            name: athlete.shift.at_css('.name').text,
            runs: athlete
            .select{ |tr| tr.attr('class') == 'run' }
            .map{|run| run.text.to_f }
        }
    end
    
    puts athletes.inspect
    #[{:name=>"Alex", :runs=>[5.0, 5.2]}, {:name=>"John", :runs=>[5.0]}]
    

    【讨论】:

      【解决方案2】:

      我会按如下方式处理表格:

      1. 找到要处理的表

        table = doc.at_css("table")
        
      2. 获取表中的所有直接行

        rows = table.css("> tr")
        
      3. .athlete.end 为边界对行进行分组

        grouped = [[]]
        rows.each do |row|
          if row['class'] == 'athlete' and grouped.last.empty?
            grouped.last << row
          elsif row['class'] == 'end' and not grouped.last.empty?
            grouped.last << row
            grouped << []
          elsif not grouped.last.empty?
            grouped.last << row
          end
        end
        grouped.pop if grouped.last.empty? || grouped.last.last['class'] != 'end'
        
      4. 处理分组的行

        grouped.each do |group|
          puts "BEGIN: >> #{group.first.text} <<"
          group[1..-2].each do |row|
            puts "  #{row.text.squeeze}"
          end
          puts "END: >> #{group.last.text} <<"
        end
        

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-10
        • 1970-01-01
        • 1970-01-01
        • 2020-11-26
        • 2017-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多