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
Большое спасибо. Надеюсь, я смогу принять 2 ответа одновременно. Очень полезно! – cqcn1991
Настоящая проблема в том, что. Я хочу получить 20 элементов страницы.Поэтому я пишу селектор с 'first (20)'. Однако у него может быть только 15 предметов. Таким образом, у оставшегося 20-массива будет 15 предметов + 5 ноль. Я не чувствую, что это можно улучшить, используя лучший селектор, но изменить «first (20)» на более правильный. Но я не знаю, как это сделать. – cqcn1991
'[] .first (2) # => []'. Вы не можете получить «15 + 5 ноль», если вы неправильно обрабатываете массив, вы получите только 15. Лучший селектор * поможет *; Это основано на большом количестве сайтов обработки опыта. Таким образом, проблема заключается не в том, как вы запрашиваете 20, это то, что вы делаете впоследствии. –