我想用Nokogiri解析一个HTML页面。页面的一部分有一个表,它不使用任何特定的ID。是否可以提取类似的内容:
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>
答案 0 :(得分:10)
作为一个快速而肮脏的第一关,我会这样做:
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”循环的退出处,today_data
和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'
,但我认为它不会改进代码,它只是一种替代方式。