Nachdem mir letze Woche die FH dazwischengekommen ist, gabs eine Woche Verspätung. Sorry.
Hpricot ist ein sehr flexibler und schneller HTML-Parser, geschrieben von _why. Mittels XPath-Abfragen kommt man schnell an die gewünschten Teile einer HTML-Seite.
Hpricot ist wie gewohnt per Rubygems verfügbar:
gem install hpricot
Unter Unixoiden Betriebssystemen wird ein C-Compiler vorausgesetzt, und Debian-User müssen wie gewohnt das ruby-dev-Paket installiert haben.
Nachdem man Hpricot mittels require eingebunden hat, kann man mittels der Hpricot()-Methode HTML parsen lassen. Diese Methode kann dabei entweder Strings oder beliebige IO-Objekte verarbeiten, wodurch sie sehr flexibel ist:
require 'rubygems' require 'hpricot' # Laden aus einem String: doc = Hpricot("<p>A simple <b>test</b> string.</p>") # Laden aus einer Datei: doc = open("index.html") { |f| Hpricot(f) } # Laden einer URL mittels open-uri: require 'open-uri' doc = Hpricot(open("http://www.ruby-mine.de/"))
Die Hpricot-Methode unterstützt dabei noch zwei (jeweils exklusive) optionale Parameter. Da Hpricot im Normalfall versucht, beim Parsen so flexibel wie möglich zu sein, wird beinahe keine Validation des HTML durchgeführt (nur das auch alle Tags geschlossen werden). Falls man aber ein Dokument haben möchte das dem "XHMTL 1.0 Strict" Standard entspricht, bietet Hpricot zwei Möglichkeiten: Zum einen gibt es :fixup_tags, welches versucht die Tags im Dokument näher an den XHTML-Standard zu bringen. Wenn es zum Beispiel einen Paragraph-Tag innerhalb eines Anchor-Tags findet, verschiebt es den Paragraph-Tag ausserhalb des Anchor-Tags. Unbekannte Tags werden einfach ignoriert. Einfach ein ":fixup_tags => true" an den Hpricot-Methodenaufruf anhängen um diesen Modus zu verwenden.
Die zweite Möglichkeit ist mittels :xhmtl_strict. Diese Option versucht mit allen Mitteln eine "XHMTL 1.0 Strict"-Dokumentstruktur zu erzeugen, und ist viel strenger als :fixup_tags. So entfernt sie auch mal kurzerhand Elemente, Attribute oder Inhalte die nicht in den Standard passen.
Sobald wir unser Dokument-Objekt haben, können wir darin suchen. Dazu gibt es entweder die Methode search oder den /-Operator als Abkürzung. Nehmen wir mal an wir möchten uns die Kategorien von allen Posts auf der Ruby Mine anschauen. Wenn wir uns das dazugehörige HTML ansehen, sehen wir dass diese Kategorien immer in <div>-Tags der Klasse "filedto" eingekapselt sind. Und so kommen wir an sie ran:
require 'rubygems' require 'hpricot' require 'open-uri' doc = Hpricot(open("http://www.ruby-mine.de/")) categories = doc.search("//div[@class='filedto']") # => #<Hpricot::Elements[{elem <div class="filedto"> "Kategorie(n): " {elem <a href="/cypher"> "cypher [Autor]" </a>} ", " {elem <a href="/tools"> "Tools" </a>} </div>}, [...]> # Alternative: doc / "div.filedto" # => #<Hpricot::Elements[{elem <div class="filedto"> "Kategorie(n): " {elem <a href="/cypher"> "cypher [Autor]" </a>} ", " {elem <a href="/tools"> "Tools" </a>} </div>}, [...]>
search verwendet dabei die XPath-Syntax, die so manchen von XML bereits bekannt sein dürfte. Das Hpricot-Wiki hat eine Liste mit den unterstützen XPath-Ausdrücken.
Wenn wir nur am ersten Element interessiert sind, bietet Hpricot die Doc.at-Methode an:
doc.at("//div[@class='filedto']") # => {elem <div class="filedto"> "Kategorie(n): " {elem <a href="/cypher"> "cypher [Autor]" </a>} ", " {elem <a href="/tools"> "Tools" </a>} </div>}
Wir haben in unserer Suche noch immer ein paar unnötige Elemente. Um genauer zu sein, wir wollen alles was zwischen den <a>-Tags steht, um die Kategorien zu bekommen. Wir ändern also unseren obigen Code etwas ab:
categories = doc.search("//div[@class='filedto']").map { |category| category.search("//a").map { |a| a.inner_html } }.flatten.uniq
Dadurch bekommen wir ein Array mit allen Kategorien die auf der Ruby Mine Seite im Moment vorkommen. Interessant dabei: Hpricot erlaubt uns verschachteltes suchen, wir müssen also nicht eine XPath-Abfrage gestalten die alle unsere gesuchten Elemente beim ersten search erwischt. In diesem Fall wäre das sogar einfacher, aber aus pädagogischer Sicht nicht sinnvoll :)
Mittels inner_html kommen wir an den ganzen Text, der zwischen unseren Tags steht. Die Methode to_html wandelt das ganze Element wieder in HTML um.
Hpricot bietet noch viel mehr als das hier vorgestellte. So kann man z.b. statt mit XPath auch per CSS-Selectors suchen, oder Elemente verändern. Das Hpricot Wiki sollte dazu eure erste Anlaufstelle sein.
Nächste Woche, falls mir nicht wieder was dazwischen kommt: Eventmachine
Kommentar schreiben
Kommentare
Au ja, Eventmachine - da wollt ich schon immer mal was drüber hören.
hi.. the hpricot wiki page is no longer existant.. http://hpricot.com is the place to go now.. regards