2016-09-28 4 views
0

Ich betreibe die folgenden erfolgreich:Kein Zugriff auf Nokogiri Element innerhalb des Blocks

require 'nokogiri' 
require 'open-uri' 

own = Nokogiri::HTML(open('https://www.sec.gov/cgi-bin/own-disp?action=getowner&CIK=0001513362')) 
own_table = own.css('table#transaction-report') 

p own_table.css('tr').css('td')[4].css('a').attr('href').value 

=> "/Archives/edgar/data/0001513362/000162828016019444/0001628280-16-019444-index.htm"

Wenn ich jedoch versuche, das obige Element in einem Block zu verwenden (wie im folgenden Code gezeigt), bekomme ich einen NoMethodError für nil: NilClass.

Ich bin verwirrt, weil ich dachte, dass die lokale Variable Link im Block das gleiche Objekt wie im obigen Code wäre.

Außerdem, wenn ich die Definition Link ändern unten an:

link = row.css ('td') [4] .class-

ich einen Hash ohne Fehler, zu sagen, den Wert Link ist Nokogiri :: XML :: Element.

Kann mir jemand erklären, warum ich ein Nokogiri :: XML :: Element-Objekt habe, aber die css-Methode nicht darauf ausführen kann. Vor allem, wenn ich es im ersten Ausschnitt ausführen kann?

require 'nokogiri' 
require 'open-uri' 

own = Nokogiri::HTML(open('https://www.sec.gov/cgi-bin/own-disp?action=getowner&CIK=0001513362')) 
own_table = own.css('table#transaction-report') 


own_table.css('tr').each do |row| 
    names = [:acq, :transaction_date, :execution_date, :issuer, :form, :transaction_type, :direct_or_indirect_ownership, :number_of_securities_transacted, :number_of_securities_owned, :line_number, :issuer_cik, :security_name, :url] 
    values = row.css('td').map(&:text) 
    link = row.css('td')[4].css('a').attr('href').value 
    values << link 
    hash = Hash[names.zip values] 
    puts hash 
end 

secown.rb:11:in `block in <main>': undefined method `css' for nil:NilClass (NoMethodError) 
    from /Users/piperwarrior/.rvm/gems/ruby-2.2.1/gems/nokogiri-1.6.7.2/lib/nokogiri/xml/node_set.rb:187:in `block in each' 
    from /Users/piperwarrior/.rvm/gems/ruby-2.2.1/gems/nokogiri-1.6.7.2/lib/nokogiri/xml/node_set.rb:186:in `upto' 
    from /Users/piperwarrior/.rvm/gems/ruby-2.2.1/gems/nokogiri-1.6.7.2/lib/nokogiri/xml/node_set.rb:186:in `each' 
    from secown.rb:8:in `<main>' 
+1

Bitte lesen Sie "[mcve]". Wenn wir nach einem Problem mit Code fragen, benötigen wir die minimalen Eingabedaten (in diesem Fall HTML), die das Problem in der Frage selbst aufzeigen. Bitten Sie uns nicht, auf eine Seite zu gehen und eine ganze Seite durchzulesen, da dies unsere Antwortzeit für Sie verlangsamt und unsere Fähigkeit beeinträchtigt, anderen zu helfen. Sie sollten niemals jedes Tag mit 'css' oder' search' verketten müssen. Verwenden Sie stattdessen komplexere Selektoren, die vom Orientierungspunkt zum Orientierungspunkt zum Ziel im Markup springen. Das ist weniger fragil. Außerdem sollten Sie länger warten, bevor Sie eine Antwort auswählen. –

Antwort

1

Die entscheidende Erkenntnis ist, dass im ersten Fall, own_table.css('tr') eine NodeSet zurückgibt, .css('td') findet alle td die Nachkomme zu beliebigen Knoten in diesem nodeset ist, dann die vierte findet man (als Programmierer gesprochen, fünften für normale Leute: P).

Das zweite Snippet behandelt jede Zeile einzeln als Node, findet dann alle Nachkommen td, wählt dann die vierte aus.

Also, wenn Sie diese Struktur:

tr id=1 
    td id=2 
    td id=3 
tr id=4 
    td id=5 
    td id=6 
    td id=7 
    td id=8 
    td id=9 

dann das erste Snippet gibt Ihnen die ID 7 td (es die vierte td in allen tr zu sein); der zweite Ausschnitt würde versuchen, die vierte td in ID 1 tr, dann vierte td in ID 4 tr zu finden, aber es ist fehlerhaft, weil id 1 tr keine vierte td hat.

Edit: Genauer gesagt, nach Überprüfung Ihrer URL hat die erste tr keine td; alle anderen haben 12. So own_table.css('tr')[0].css('td')[4].class ist , nicht Nokogiri::XML::Element wie Sie berichten.

+0

Ich meinte ändern lokalen Link in Block zu verknüpfen = row.css ('td') [4]. Klasse gibt Nokogiri :: XML :: Element – PiperWarrior

+1

Ich weiß, was Sie meinten. Es ist für 80 Zeilen (die mit 12 'td'); nicht für den ersten (der ohne 'td'). – Amadan

+1

@Amadan hat recht, die erste Zeile hat ein 'th' anstelle von' td', weil du 'NoMethodError' ' values ​​= row.css ('td') bekommst. Map (&: text) => [] ' – retgoat

1

Bedenken Sie:

require 'nokogiri' 

doc = Nokogiri::HTML(<<EOT) 
<html> 
    <body> 
    <div><span><p>foo</p></span></div> 
    <div id="bar"><span><p>bar</p></span></div> 
    </body> 
</html> 
EOT 

Wenn ich die Kette, die Methoden, die ich alle passenden <p> Knoten innerhalb der <div> s finden werde:

doc.css('div').css('span').css('p').to_html 
# => "<p>foo</p><p>bar</p>" 

oder:

doc.css('div').css('p').to_html 
# => "<p>foo</p><p>bar</p>" 

Das entspricht der Verwendung der folgenden Selektoren, nur sind sie ein wenig effizienter als th ey beinhalten keine libXML mehrfach Aufruf:

doc.css('div span p').to_html 
# => "<p>foo</p><p>bar</p>" 

oder:

doc.css('div p').to_html 
# => "<p>foo</p><p>bar</p>" 

Wirklich sollten Sie Sehenswürdigkeiten in der Ziel Markup und LeapFrog von Tag-Schritt finden von einem zum nächsten, nicht zu markieren:

doc.css('#bar p').to_html 
# => "<p>bar</p>" 

Wenn Ihre Absicht, alle Übereinstimmungen zu finden war dann ersetzen #bar mit div in dem obigen Selektor und es wird die Suche lockern.

Schließlich, wenn Ihr Ziel ist, den Text eines Satzes von Knoten zu extrahieren ist, wollen Sie nicht wie etwas verwenden:

doc.css('bar p').text 

css, wie search und xpath Gibt eine nodeset und text wird verketten der Text von allen zurückgegebenen Knoten, wodurch es schwierig wird, den Text von den einzelnen Knoten abzurufen. Stattdessen verwenden:

doc.css('bar p').map(&:text) 

, die eine Anordnung zurückkehren, den Text eines jeden Knotens Enthalten gefunden:

doc.css('div p').text 
# => "foobar" 

VERSUS:

doc.css('div p').map(&:text) 
# => ["foo", "bar"] 

Siehe "How to avoid joining all text from Nodes when scraping" ebenfalls.

+0

In dem obigen Code funktioniert doc.css ('div') .css ('span') .css ('p') .to_html nicht wie angegeben (es gibt eine leere Zeichenfolge zurück). Wenn Sie css ('span') entfernen, tut es das. – PiperWarrior

+0

Sehr instruktive Post. Der Code sieht sauberer aus und wiederholt sich nicht, wenn die Verkettung aufgehoben wird. – PiperWarrior

+0

'Wenn Sie entfernen, css ('span')' ... Das ist, weil ich vergessen habe, das Beispiel auf die Version mit den 'span' Tags zu aktualisieren. Ich werde es aktualisieren. –

Verwandte Themen