2014-10-02 3 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? Мне не нужны нильсы.

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'] 

это плохая практика.

Вместо этого следует писать с использованием at или at_css вместо search или css:

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

Далее, если <a> тег возвращается не имеет параметр title, Nokogiri и/или рубин будет расстроен, потому что 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 их из возвращаемого массива.

Как общий совет по программированию, если вы получаете такие нули, посмотрите на код, генерирующий массив, потому что коэффициенты хороши, но это не так. Вы должны ALWAYS знаете, какие результаты ваш код будет генерировать. Использование 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 

В Нокигири, 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>"] 

Это показывает, что NodeSet вернулся, делая простой 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» элементов. Если этого не найдено, Нокогири заполняет их при использовании значений nil.

Это счетчик, который мы предположили бы first(n), так как Enumerable#first возвращает up-to-n и не будет заполнять нил. Это не ошибка, но это непредвиденное поведение, так как Enumerable устанавливает ожидаемое поведение для методов с этим именем, но это 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

+0

Настоящая проблема в том, что. Я хочу получить 20 элементов страницы.Поэтому я пишу селектор с 'first (20)'. Однако у него может быть только 15 предметов. Таким образом, у оставшегося 20-массива будет 15 предметов + 5 ноль. Я не чувствую, что это можно улучшить, используя лучший селектор, но изменить «first (20)» на более правильный. Но я не знаю, как это сделать. – cqcn1991

+1

'[] .first (2) # => []'. Вы не можете получить «15 + 5 ноль», если вы неправильно обрабатываете массив, вы получите только 15. Лучший селектор * поможет *; Это основано на большом количестве сайтов обработки опыта. Таким образом, проблема заключается не в том, как вы запрашиваете 20, это то, что вы делаете впоследствии. –

1

Попробуйте

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

И дайте мне знать, что работает или нет? Он не будет показывать ошибки.

+1

СММ или просто 'название = item.css ("а") [0] [ 'название'], если item' – mhutter

+0

@Manuel Я думал, что трюк должен быть в методе «first» .... – cqcn1991

+0

Да, извините, я просто пропустил первую и последнюю строку вашего ответа в своем комментарии. Конечно, моя линия должна идти между ними. – mhutter

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' - это бандана для исправления реальной проблемы, которая не использует подходящий селектор. Исправьте селектор, и нильс исчезнет, ​​удалив необходимость использовать 'compact'. –

Смежные вопросы