В качестве быстрого и грязного первого прохода я бы сделал:
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'
, но я не думаю, что это улучшит код, это будет просто альтернативный способ сделать это.