2014-10-02 45 views
0

我正在使用Nokogiri來屏幕抓取網站的內容。在前10位中查找?

我設置了fetch_number來指定我想檢索的<divs>的數量。例如,我可能需要first(10)來自目標頁面的推文。

的代碼看起來是這樣的:

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

但是,當小於10個匹配div標籤返回,它將報告

NoMethodError: undefined method 'css' for nil:NilClass 

這是因爲,當沒有匹配的HTML被發現,它將返回零。

如何讓它返回10以內的所有可用數據?我不需要nils。

UPDATE:

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 

返回resultes(接近端):

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個元件,但它retuns 30陣列。 它實際上不是一個數組,但是Nokogiri::XML::NodeSet?我不確定。

回答

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

是一種不好的做法。

而應考慮使用atat_css代替searchcss寫着:

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

接下來,如果返回的<a>標籤沒有一個title參數,引入nokogiri和/或Ruby將是苦惱的原因, title變量將爲零。相反,提高你的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的參數。

這意味着你的循環遍歷值永遠不會返回零,並且你不會有返回的數組需要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只返回一個節點,什麼節點樣子。

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返回最多爲n,並且不會填充nils。這是不是一個錯誤,但它是意外的行爲,因爲可枚舉的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 
+0

非常感謝。我希望我能同時接受2個答案。很有幫助! – cqcn1991 2014-10-03 13:39:03

+0

真正的問題是這樣的。我想獲得一頁20個項目。所以我用'first(20)'編寫一個選擇器。但是,它可能只有15個項目。所以剩餘的20個陣列將有15個項目+ 5個零。我不覺得這可以通過使用更好的選擇器來改進,而是將「第一(20)」更改爲更合適的方式。但我不知道如何。 – cqcn1991 2014-10-03 13:50:18

+1

'[] .first(2)#=> []'。除非你不正確地處理數組,否則你不能得到「15 + 5無」,你只能得到15。這是基於很多經驗處理網站。所以,問題不在於你如何請求20,而是你在做什麼。 – 2014-10-03 16:41:03

1

試試這個

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

讓我知道它的工作原理或不?它不會顯示錯誤

+1

HMM或只是'標題= item.css( 「A」)[0] [ '標題']如果item' – mhutter 2014-10-02 10:18:36

+0

@Manuel我認爲招應該在'first'方法.... – cqcn1991 2014-10-02 10:20:55

+0

是的,對不起,我只是中省略你的答案的第一個和最後一行在我的評論。當然,我的路線應該介入他們之間。 – mhutter 2014-10-02 10:23:46

1

嘗試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|

+1

使用'compact'是一個bandaid來修補真正的問題,它沒有使用適當的選擇器。修復選擇器和nils將消失,消除使用'compact'的需要。 – 2014-10-02 23:58:52