2014-10-02 2 views
0

Nokogiri를 사용하여 웹 사이트의 내용을 스크랩했습니다.처음 10 개 이내에 찾으 시나요?

나는 <divs>의 숫자를 지정하기 위해 fetch_number을 설정했습니다. 예를 들어 대상 페이지의 first(10) 트윗을 원할 수 있습니다.

코드는 다음과 같습니다

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

을하지만, 미만 10 개 일치 div 태그가 반환있을 때, 그것은이가 있기 때문에 일치하는 HTML이 발견되지 않는 경우

NoMethodError: undefined method 'css' for nil:NilClass 

보고 , 그것은 nil을 반환 할 것이다.

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 원소지만 30 어레이 retuns. 실제로 배열이 아니지만 Nokogiri::XML::NodeSet? 나는 잘 모르겠다.

답변

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

나쁜 방법입니다. 대신

, at 또는 at_css 대신 search 또는 css를 사용하여 작성 고려해 반환 <a> 태그가 title 매개 변수가없는 경우

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

다음, 노코 기리 및/또는 루비 화가 될 것입니다 때문에 title 변수는 0이됩니다. 대신에, 단지 <a title="foo">처럼 일치를 허용하도록 CSS 선택기를 개선 : 제한되지 않는, 첫 번째는 title 매개 변수를 사용하여 태그를 찾는 방법

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>" 

공지 사항 첫 번째 <a> 태그를 반환합니다. a[title]을 사용하면 title 매개 변수가있는 매개 변수 만 반환됩니다.

즉, 값을 둘러싼 루프는 결코 nil을 반환하지 않으므로 반환 된 배열에서 compact 개의 문제가 발생하지 않아도됩니다.

일반적으로 프로그래밍 팁 에서처럼 nils가 좋으면 확률이 좋기 때문에 배열을 생성하는 코드를 살펴보십시오. 항상 코드가 어떤 결과를 생성하는지 알고 있어야합니다. 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 

그리고 여기 뭐가 잘못이다 :

require 'nokogiri' 

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

:

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

여기서 문제가 해결되지 않는 이유를 설명하는 간단한 예제 Nokogiri에서 search', css and xpath`는 첫 번째가 제네릭이고 CSS 또는 XPath 중 하나를 사용할 수 있다는 것을 제외하고는 마지막 두 개는 해당 언어에만 해당합니다. 노드 집합 간단한 search 반환에게 하나 개의 노드 만, 어떤 노드의 모양을 수행하여 반환 된 것을 알 수

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

.

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가 nil 값을 사용하여 채 웁니다.

Enumerable#first이 n을 반환하고 nils로 채우지 않으므로이 값이 first(n)이라고 가정 할 때 카운터입니다. 이것은 버그가 아니지만 Enumerable의 first이 해당 이름의 메소드에 대해 예상되는 동작을 설정했기 때문에 예기치 않은 동작이지만 NodeSet#first이 아니라 Enumerable#first이 아니므로 Nokogiri 작성자가 변경하기 전까지 수행하는 작업을 수행합니다. (당신이 특정 방법에 대한 소스를 보면 그런 일이 이유를 확인할 수 있습니다.)

을 대신 이 예상되는 동작 보여 않는 노드 세트를 얇게 : 그래서

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 nil"을 얻을 수 없습니다. 단지 15를 얻게됩니다. 더 나은 선택자가 * 도움을 줄 것입니다. 이것은 많은 경험 처리 사이트를 기반으로합니다. 그래서, 문제는 당신이 20을 요청하는 방법이 아니라, 당신이 나중에하고있는 것입니다. –

1

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

을 시도하고 나 작동 여부를 알려주세요? 오류를 표시하지 않습니다

+1

흠 또는 '제목 = item.css ("A") [0] ['제목 '] item' 경우 – mhutter

+0

@Manuel : 그 적용

, 내가 좋아하는 코드 뭔가를 써서 트릭이 '첫 번째'방법으로 이루어져야한다고 생각했습니다 .... – cqcn1991

+0

예, 죄송합니다. 내 의견에 처음과 마지막 줄을 생략했습니다. 당연히 나의 선은 inbetween 그 사이에 가야한다. – 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 '는 적절한 선택자를 사용하지 않고 실제 문제를 패치하기위한 bandaid입니다. 선택기를 고칠 때 nils가 사라져서 'compact'를 사용할 필요가 없습니다. –

관련 문제