Html 为什么每个表行都有一条空白记录?

Html 为什么每个表行都有一条空白记录?,html,parsing,screen-scraping,nokogiri,Html,Parsing,Screen Scraping,Nokogiri,由于另一个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

由于另一个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任务 导致此代码的另一个问题是“.”

根据注释:


我想你可能是对的,我已经在远程网页上查看了HTML,他们正在为每个分配了类的表行添加一个包装。我想知道是否有办法让脚本跳过空行

如果您看到的HTML结构如下:

<table>
  <tbody>
    <tr>
      <tr>
        <td>time</td>
        <td>source</td>
        <td>destination</td>
        <td>duration</td>
      </tr>
    </tr>
  </tbody>
</table>
<> P>获取空白行的原因是HTML格式错误。外面的
不应该在那里。修复很简单,而且可以使用正确的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, source, destination, duration = row.search('td').map{ |td| td.text.strip }
运行该代码将输出您想要的:

{:time=>"time",
 :source=>"source",
 :destination=>"destination",
 :duration=>"duration"}
所以事情还是很好

您的原始代码存在以下问题:

css
search
的别名。Nokogiri为这两个节点返回一个节点集
text
将从一个空节点集返回一个空字符串,您可以从查看外部
行.css(“td:nth child(…)”).text.strip调用中得到该空字符串。所以,Nokogiri没有默默地做你想做的事情,因为它在语法上是正确的,在逻辑上也是正确的,因为你告诉它要做什么;它只是没有达到你的期望

使用
at
,或其别名之一,如
css\u at
,查找第一个匹配的访问器。因此,理论上,我们可以继续使用
row.at(“td:nth child(1)”).text.strip
,为每个访问者分配多个任务,这会立即暴露出HTML有问题,因为
text
会爆炸。但这还不够像禅宗

相反,我们可以使用
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
将其改装到您的代码中,您将获得:

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

您可能不需要
页面=

我怀疑HTML中有某种东西导致了中间循环,但没有填充值,例如没有内容的附加行。我认为您可能是对的,我查看了远程网页上的HTML,他们正在为分配了类的每个表行添加一个包装。我想知道是否有办法让脚本跳过空行?添加一个HTML示例,其中包含两行和单元格。那我们就可以解决问题了。没有这些,我们只是猜测。确保。看看下面我的答案。哇,写得好-谢谢!我现在要好好读一遍,试试你的建议。
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