2011-06-04 66 views
5

我想用Nokogiri解析HTML页面。页面的一部分没有使用任何特定的ID。是否可以提取类似:如何用Nokogiri解析纯HTML表格?

Today,3,455,34 
Today,1,1300,3664 
Today,10,100000,3444, 
Yesterday,3454,5656,3 
Yesterday,3545,1000,10 
Yesterday,3411,36223,15 

从这个HTML:

<div id="__DailyStat__"> 
    <table> 
    <tr class="blh"><th colspan="3">Today</th><th class="r" colspan="3">Yesterday</th></tr> 
    <tr class="blh"><th>Qnty</th><th>Size</th><th>Length</th><th class="r">Length</th><th class="r">Size</th><th class="r">Qnty</th></tr> 
    <tr class="blr"> 
     <td>3</td> 
     <td>455</td> 
     <td>34</td> 
     <td class="r">3454</td> 
     <td class="r">5656</td> 
     <td class="r">3</td> 
    </tr> 

    <tr class="bla"> 
     <td>1</td> 
     <td>1300</td> 
     <td>3664</td> 
     <td class="r">3545</td> 
     <td class="r">1000</td> 
     <td class="r">10</td> 
    </tr> 

    <tr class="blr"> 
     <td>10</td> 
     <td>100000</td> 
     <td>3444</td> 
     <td class="r">3411</td> 
     <td class="r">36223</td> 
     <td class="r">15</td> 
    </tr> 
    </table> 
</div> 

回答

9

作为一个快速和肮脏的第一遍我会怎么做:

html = <<EOT 
<div id="__DailyStat__"> 
    <table> 
    <tr class="blh"><th colspan="3">Today</th><th class="r" colspan="3">Yesterday</th></tr> 
    <tr class="blh"><th>Qnty</th><th>Size</th><th>Length</th><th class="r">Length</th><th class="r">Size</th><th class="r">Qnty</th></tr> 
    <tr class="blr"> 
     <td>3</td> 
     <td>455</td> 
     <td>34</td> 
     <td class="r">3454</td> 
     <td class="r">5656</td> 
     <td class="r">3</td> 
    </tr> 

    <tr class="bla"> 
     <td>1</td> 
     <td>1300</td> 
     <td>3664</td> 
     <td class="r">3545</td> 
     <td class="r">1000</td> 
     <td class="r">10</td> 
    </tr> 

    <tr class="blr"> 
     <td>10</td> 
     <td>100000</td> 
     <td>3444</td> 
     <td class="r">3411</td> 
     <td class="r">36223</td> 
     <td class="r">15</td> 
    </tr> 
    </table> 
</div> 
EOT 

# Today    Yesterday 
# Qnty Size Length Length Size Qnty 
# 3 455 34  3454 5656 3 
# 1 1300 3664 3545 1000 10 
# 10 100000 3444 3411 36223 15 


require 'nokogiri' 

doc = Nokogiri::HTML(html) 

使用CSS找到表格的开始,并定义一些地方以容纳我们正在捕获的数据:

table = doc.at('div#__DailyStat__ table') 

today_data  = [] 
yesterday_data = [] 

遍历在表中的行,拒绝该标头:

table.search('tr').each do |tr| 

    next if (tr['class'] == 'blh') 

初始化阵列以从每个行捕捉相关数据,选择性地将数据推入相应的数组:

today_td_data  = [ 'Today'  ] 
    yesterday_td_data = [ 'Yesterday' ] 

    tr.search('td').each do |td| 
    if (td['class'] == 'r') 
     yesterday_td_data << td.text.to_i 
    else 
     today_td_data << td.text.to_i 
    end 
    end 

    today_data  << today_td_data 
    yesterday_data << yesterday_td_data 

end 

而输出数据:

puts today_data.map{ |a| a.join(',') } 
puts yesterday_data.map{ |a| a.join(',') } 

> Today,3,455,34 
> Today,1,1300,3664 
> Today,10,100000,3444 
> Yesterday,3454,5656,3 
> Yesterday,3545,1000,10 
> Yesterday,3411,36223,15 

只是为了帮助您可视发生了什么事情,在出口处从“TR”循环中,阵列阵列的-3210个yesterday_data阵列看起来像:

[["Today", 3, 455, 34], ["Today", 1, 1300, 3664], ["Today", 10, 100000, 3444]] 

另一方面,不是循环在“TD”标签和感应的标签类,我可以抓住的“TR”的内容然后用scan抢号和切片结果数组到“今天”和“昨天”阵列:

tr_data = tr.text.scan(/\d+/).map{ |i| i.to_i } 

    today_td_data  = [ 'Today',  *tr_data[0, 3] ] 
    yesterday_td_data = [ 'Yesterday', *tr_data[3, 3] ] 

在现实世界中的发展,如在工作中,我第一次用这个来代替我因为它很简洁而写了。

并注意到我没有使用XPath。在Nokogiri中使用XPath并实现这一点非常实用,但为了简单起见,我更喜欢CSS访问器。 XPath允许访问单独的“td”标签内容,但它也开始看起来像线噪声,这是我们在编写代码时要避免的,因为它会影响维护。我也可以使用CSS深入到正确的“td”标签,如'tr td.r',但我认为它不会改进代码,它只是一种替代方法。

+0

谢谢@锡人。它工作正常,只是因为得到一些错误,我必须将doc.at更改为doc.css。我有一些小问题,我会尽力解决。我没有足够的声望来投票,所以振作起来:) – JraNil 2011-06-04 20:28:21

+0

'at'与CSS和XPath一起工作,但只返回节点的第一次出现,所以您可能有多个'table'标记。 'css'是'search'的别名,它返回一个NodeSet,AKA是一个节点数组,所以你必须索引结果或迭代它们。 'at_css'是与'at'等效的CSS。 – 2011-06-04 20:32:34

+0

@JraNil此外,作为参考,代码与您的示例HTML一起工作,因此示例中必须缺少某些内容。如果您生成准确的样本,我们可以提供更好的答案。 – 2011-06-04 20:39:48