在前10个中查找



我正在使用Nokogiri来筛选网站的抓取内容。

我将fetch_number设置为指定要检索的<divs>的数目。例如,我可能想要来自目标页面的first(10)推文。

代码如下:

doc.css(".tweet").first(fetch_number).each do |item|
  title = item.css("a")[0]['title']
end

然而,当返回的匹配div标签少于10个时,它将报告

NoMethodError: undefined method 'css' for nil:NilClass

这是因为,当找不到匹配的HTML时,它将返回nil。

如何使它在10内返回所有可用数据?我不需要尼罗河。

更新:

task :test_fetch => :environment do
  require 'nokogiri'
  require 'open-uri'
  url = 'http://themagicway.taobao.com/search.htm?&search=y&orderType=newOn_desc'
  doc = Nokogiri::HTML(open(url) )
  puts doc.css(".main-wrap .item").count
  doc.css(".main-wrap .item").first(30).each do |item_info|
    if item_info
      href = item_info.at(".detail a")['href']
      puts href
    else
      puts 'this is empty'
    end
  end
end

返回结果(接近结束):

24
http://item.taobao.com/item.htm?id=41249522884
http://item.taobao.com/item.htm?id=40369253621
http://item.taobao.com/item.htm?id=40384876796
http://item.taobao.com/item.htm?id=40352486259
http://item.taobao.com/item.htm?id=40384968205
.....
http://item.taobao.com/item.htm?id=38843789106
http://item.taobao.com/item.htm?id=38843517455
http://item.taobao.com/item.htm?id=38854788276
http://item.taobao.com/item.htm?id=38825442050
http://item.taobao.com/item.htm?id=38630599372
http://item.taobao.com/item.htm?id=38346270714
http://item.taobao.com/item.htm?id=38357729988
http://item.taobao.com/item.htm?id=38345374874
this is empty
this is empty
this is empty
this is empty
this is empty
this is empty

count只报告了24个元素,但它重新运行了一个30个数组。它实际上不是一个数组,而是Nokogiri::XML::NodeSet?我不确定。

title = item.css("a")[0]['title']

这是一种不好的做法。

相反,考虑使用atat_css而不是searchcss:进行编写

title = item.at('a')['title']

接下来,如果返回的<a>标记没有title参数,那么Nokogiri和/或Ruby将被打乱,因为title变量将为nil。相反,改进你的CSS选择器,只允许像<a title="foo">:这样的匹配

require 'nokogiri'
doc = Nokogiri::HTML('<body><a href="foo">foo</a><a href="bar" title="bar">bar</a></body>')
doc.at('a').to_html # => "<a href="foo">foo</a>"
doc.at('a[title]').to_html # => "<a href="bar" title="bar">bar</a>"

请注意,第一个不限于查找具有title参数的标记,它是如何返回第一个<a>标记的。使用a[title]将只返回带有title参数的值。

这意味着您对值的循环永远不会返回nil,并且您不需要从返回的数组中compact它们。

作为一个通用的编程技巧,如果你得到了这样的幂,看看生成数组的代码,因为很有可能它做得不对。您应该始终知道您的代码将生成什么样的结果。使用compact清理数组是对大部分时间没有正确编写代码的下意识反应。


这是您的更新代码:

require 'nokogiri'
require 'open-uri'
url = 'http://themagicway.taobao.com/search.htm?&search=y&orderType=newOn_desc'
doc = Nokogiri::HTML(open(url) )
puts doc.css(".main-wrap .item").count
doc.css(".main-wrap .item").first(30).each do |item_info|
  if item_info
    href = item_info.at(".detail a")['href']
    puts href
  else
    puts 'this is empty'
  end
end

以下是问题所在:

doc.css(".main-wrap .item").first(30)

这里有一个简单的例子来说明为什么它不起作用:

require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<html>
<body>
<p>foo</p>
</body>
</html>
EOT

在Nokogiri中,search', css and xpath `是等效的,只是第一个是通用的,可以采用css或xpath,而后两个是特定于该语言的。

doc.search('p') # => [#<Nokogiri::XML::Element:0x3fcf360ef750 name="p" children=[#<Nokogiri::XML::Text:0x3fcf360ef4f8 "foo">]>]
doc.search('p').size # => 1
doc.search('p').map(&:to_html) # => ["<p>foo</p>"]

这表明,通过执行简单的search返回的NodeSet只返回一个节点,以及该节点的样子。

doc.search('p').first(2) # => [#<Nokogiri::XML::Element:0x3fe3a28d2848 name="p" children=[#<Nokogiri::XML::Text:0x3fe3a28c7b50 "foo">]>, nil]
doc.search('p').first(2).size # => 2

使用first(n)进行搜索会返回"n"个元素。如果没有找到那么多,Nokogiri会使用零值填充它们。

这与我们假设first(n)所做的相反,因为Enumerable#first返回up-to-n,并且不会用nil填充。这不是一个错误,但这是一个意外的行为,因为Enumerable的first为具有该名称的方法设置了预期的行为,但是,这是NodeSet#first,而不是Enumerable#first,所以它会做它所做的事情,直到Nokogiri作者更改它

相反,对NodeSet进行切片确实显示了预期的行为:

doc.search('p')[0..1] # => [#<Nokogiri::XML::Element:0x3fe3a28d2848 name="p" children=[#<Nokogiri::XML::Text:0x3fe3a28c7b50 "foo">]>]
doc.search('p')[0..1].size # => 1
doc.search('p')[0, 2] # => [#<Nokogiri::XML::Element:0x3fe3a28d2848 name="p" children=[#<Nokogiri::XML::Text:0x3fe3a28c7b50 "foo">]>]
doc.search('p')[0, 2].size # => 1

所以,不要使用NodeSet#first(n),使用切片形式NodeSet#[]

应用它,我会写这样的代码:

require 'nokogiri'
require 'open-uri'
URL = 'http://themagicway.taobao.com/search.htm?&search=y&orderType=newOn_desc'
doc = Nokogiri::HTML(open(URL))
hrefs = doc.css(".main-wrap .item .detail a[href]")[0..29].map { |anchors|
  anchors['href']
}
puts hrefs.size
puts hrefs
# >> 24
# >> http://item.taobao.com/item.htm?id=41249522884
# >> http://item.taobao.com/item.htm?id=40369253621
# >> http://item.taobao.com/item.htm?id=40384876796
# >> http://item.taobao.com/item.htm?id=40352486259
# >> http://item.taobao.com/item.htm?id=40384968205
# >> http://item.taobao.com/item.htm?id=40384816312
# >> http://item.taobao.com/item.htm?id=40384600507
# >> http://item.taobao.com/item.htm?id=39973451949
# >> http://item.taobao.com/item.htm?id=39861209551
# >> http://item.taobao.com/item.htm?id=39545678869
# >> http://item.taobao.com/item.htm?id=39535371171
# >> http://item.taobao.com/item.htm?id=39509186150
# >> http://item.taobao.com/item.htm?id=38973412667
# >> http://item.taobao.com/item.htm?id=38910499863
# >> http://item.taobao.com/item.htm?id=38942960787
# >> http://item.taobao.com/item.htm?id=38910403350
# >> http://item.taobao.com/item.htm?id=38843789106
# >> http://item.taobao.com/item.htm?id=38843517455
# >> http://item.taobao.com/item.htm?id=38854788276
# >> http://item.taobao.com/item.htm?id=38825442050
# >> http://item.taobao.com/item.htm?id=38630599372
# >> http://item.taobao.com/item.htm?id=38346270714
# >> http://item.taobao.com/item.htm?id=38357729988
# >> http://item.taobao.com/item.htm?id=38345374874

试试这个

doc.css(".tweet").first(fetch_number).each do |item|
  title = item.css("a")[0]['title'] rescue nil
end

让我知道它是否有效?它不会显示错误

尝试compact

[1, nil, 2, nil, 3] # => [1, 2, 3]

http://www.ruby-doc.org/core-2.1.3/Array.html#method-i-compact

(即:first(fetch_number).compact.each do |item|

相关内容

  • 没有找到相关文章