На основании комментария:
Я думаю, вы могли бы быть правы, я посмотрел HTML-код на удаленной веб-странице, и они добавляют обтекание каждой строки таблицы, которой присвоен класс.Интересно, есть ли способ заставить скрипт пропустить пустые строки?
Если вы видите структуру HTML вроде:
<table>
<tbody>
<tr>
<tr>
<td>time</td>
<td>source</td>
<td>destination</td>
<td>duration</td>
</tr>
</tr>
</tbody>
</table>
Тогда это покажет проблему:
require 'nokogiri'
require 'pp'
html = '<table><tbody><tr><tr><td>time</td><td>source</td><td>destination</td><td>duration</td></tr></tr></tbody></table>'
doc = Nokogiri::HTML(html)
page = doc.search("table tbody tr").each do |row|
time = row.css("td:nth-child(1)").text.strip
source = row.css("td:nth-child(2)").text.strip
destination = row.css("td:nth-child(3)").text.strip
duration = row.css("td:nth-child(4)").text.strip
hash = {
:time => time,
:source => source,
:destination => destination,
:duration => duration
}
pp hash
end
Это выводит:
{:time=>"", :source=>"", :destination=>"", :duration=>""}
{:time=>"time",
:source=>"source",
:destination=>"destination",
:duration=>"duration"}
Причина, по которой вы получаете пустые строки, в том, что HTML-код искажен.Снаружи <tr>
не должно быть там.Исправление легко и будет работать с HTML, который также корректен.
Кроме того, внутренний доступ css
не совсем корректен, но почему это так неуловимо.Я вернусь к этому.
Чтобы исправить первое, мы добавим условный тест:
page = doc.search("table tbody tr").each do |row|
становится:
page = doc.search("table tbody tr").each do |row|
next if (!row.at('td'))
После запуска,вывод теперь такой:
{:time=>"time",
:source=>"source",
:destination=>"destination",
:duration=>"duration"}
Это действительно все, что вам нужно, чтобы решить проблему, но в коде есть некоторые вещи, которые делают сложные вещи, которые требуют некоторого «сплайна», но сначала вот кодизменить:
С:
time = row.css("td:nth-child(1)").text.strip
source = row.css("td:nth-child(2)").text.strip
destination = row.css("td:nth-child(3)").text.strip
duration = row.css("td:nth-child(4)").text.strip
Изменить на:
time, source, destination, duration = row.search('td').map{ |td| td.text.strip }
Запуск этого кода выводит то, что вы хотите:
{:time=>"time",
:source=>"source",
:destination=>"destination",
:duration=>"duration"}
так чтовсе еще неуклюжие.
Вот проблема с вашим исходным кодом:
css
является псевдонимом search
.Nokogiri возвращает NodeSet для обоих.text
вернет пустую строку из пустого NodeSet, которую вы получите за каждый из вызовов row.css("td:nth-child(...)").text.strip
, которые смотрели на внешний <tr>
.Итак, Нокогири не смог сделать то, что вы хотели молча, потому что это было синтаксически правильно и логически правильно, учитывая то, что вы сказали ему делать;Он просто не оправдал ваших ожиданий.
Использование at
или одного из его псевдонимов, например css_at
, ищет первый соответствующий метод доступа.Таким образом, теоретически мы могли бы продолжать использовать row.at("td:nth-child(1)").text.strip
с несколькими присваиваниями для каждого метода доступа, и это сразу показало бы, что у вас были проблемы с HTML, потому что text
взорвалась бы.Но этого недостаточно для дзен.
Вместо этого мы можем перебрать ячейки, возвращенные в NodeSet, используя map
, и позволить ему собрать необходимое содержимое ячейки и удалить его, а затем выполнить параллельное присваивание переменным:
time, source, destination, duration = row.search('td').map{ |td| td.text.strip }
Опять же, запустив это:
require 'nokogiri'
require 'pp'
html = '<table><tbody><tr><tr><td>time</td><td>source</td><td>destination</td><td>duration</td></tr></tr></tbody></table>'
doc = Nokogiri::HTML(html)
page = doc.search("table tbody tr").each do |row|
next if (!row.at('td'))
time, source, destination, duration = row.search('td').map{ |td| td.text.strip }
hash = {
:time => time,
:source => source,
:destination => destination,
:duration => duration
}
pp hash
end
Дает мне:
{:time=>"time",
:source=>"source",
:destination=>"destination",
:duration=>"duration"}
Модифицируйте это в свой код, и вы получите:
page = agent.page.search("table tbody tr").each do |row|
next if (!row.at('td'))
time, source, destination, duration = row.search('td').map{ |td| td.text.strip }
Call.create!(:time => time, :source => source, :destination => destination, :duration => duration)
end
И вам, вероятно, не нужны page =
.