为什么我得到一个空白记录的每一行表



由于另一个SO问题/答案,我有以下代码:

page = agent.page.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
  Call.create!(:time => time, :source => source, :destination => destination, :duration => duration)
end

它运行得很好,当我运行rake任务时,它会正确地将数据放入Rails应用程序中正确的表行,然而,由于某种原因,在成功创建一行的记录后,它也会创建一个空白记录。

我想不通。从代码的外观来看,它在每行中发出create!命令。

您可以在上查看完整的rake任务https://gist.github.com/1574942和导致这段代码的另一个问题是"每次都在没有新记录的情况下将html解析到Rails中?"。

基于注释:

我想你可能是对的,我已经查看了远程网页上的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"}

这就是你真正需要解决的问题,但代码中有一些东西正在以艰难的方式做事,这需要一些"splainin",但首先是代码更改:

发件人:

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"}

所以一切都很顺利。

以下是您的原始代码的问题:

csssearch的别名。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 =

相关内容

  • 没有找到相关文章

最新更新