根据评论:
我认为您可能是对的,我查看了远程网页上的 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 中返回一个空字符串,对于每个查看外部 <tr> 的 row.css("td:nth-child(...)").text.strip 调用,您都会获得该字符串。所以,Nokogiri 没有默默地做你想做的事,因为它在语法上和逻辑上都是正确的,因为你告诉它要做什么;它只是未能达到您的期望。
使用at 或其别名之一,如css_at,查找第一个匹配的访问器。因此,理论上我们可以继续使用 row.at("td:nth-child(1)").text.strip 并为每个访问器分配多个分配,这会立即表明您的 HTML 存在问题,因为 text 会炸毁。但这还不够禅意。
相反,我们可以使用 map 遍历 NodeSet 中返回的单元格,让它收集所需的单元格内容并剥离它们,然后对变量进行并行赋值:
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 =。