Webcrawler in Clojure

Hi,

ich habe einen Webcrawler in Clojure geschrieben und fände es schön, wenn ihr mal drüberschauen würdet, ob ich da noch etwas besser machen könnte (Übersichtlichkeit, “funktionalere” Vorgehensweise, etc.)

[clojure]
(ns verkehrsblatt.core
(:gen-class)
(:require [clojure.xml :as xml]
[net.cgrand.enlive-html :as html]
)
)

(defrecord Aufbietung
[fahrzeugbrief anzahl-halter fin hersteller datum]
)

(def uri “http://www.verkehrsblatt.de/docs/aufbietungen/suche.php?Fahrzeugbriefnummer=&FahrzeugID=&Datum=&Zeitraum_von=01.08.2013&Zeitraum_bis=01.08.2013&SuchZeitraum=Suchen#liste”)

(defn get-data
(html/select (html/html-resource (java.net.URL. url)) #{[:.tabelle5 :tr]})
)

(defn columns-for-row [row]
(Aufbietung.
(:content (nth row 1))
(:content (:content (nth row 2)))
(first (:content (nth row 3)))
(:content (:content (nth row 4)))
(:content (nth row 5))
)
)

(defn -main [& args]
(map #(columns-for-row (:content %)) (filter #(.startsWith (:class (:attrs %)) “zeile”) (get-data uri))
)
)
[/clojure]

Gecrawlt wird die Seite http://www.verkehrsblatt.de/docs/aufbietungen/suche.php?Fahrzeugbriefnummer=&FahrzeugID=&Datum=&Zeitraum_von=01.08.2013&Zeitraum_bis=01.08.2013&SuchZeitraum=Suchen#liste, da ein bekannter von mir hier regelmäßig nach “verloren” gemeldeten Fahrzeugbriefen suchen muss. Zurzeit tut er dies händisch und er hat mich gefragt, ob so etwas auch durch Software möglich ist. Generell bin ich kein Fan von Crawlern, aber er ist sich im klaren, dass es Probleme geben kann, wenn die Webseite geändert wird und es fällt durch den Crawler auch nicht mehr Traffic für die Seite an, da finde ich das schon okay.

Da er mir noch nicht sagen konnte in welchem Format er die Datensätze gerne hätte speichere ich sie vorübergehend in einem Record, an dieser Stelle wird später direkt als Excel Datei oder in eine Datenbank geschrieben. Auch das “zu crawlende” Datum wird natürlich später über Parameter abgefragt, oder je nachdem das heutige ausgewählt.

Gruß,
Tim

Suche mal. Nach clojure + deconstructuring.

Damit kannst du das col-for-row evtl verbessern. Das nth sieht auf dauer daemlich aus.

(defn c-4-r [_ a b c d e ]
(Aufbietung
(:content a)
(:content (:content b))
…)

Ausserdem solltest du der schliessenden klammer der fn keine neue Zeile spendieren.
Das braucht zuviel Platz, da eh eine Leerzeile kommt. Dann passt bei guter lesbarkeit mehr auf den screen.

Die uri ist imho in der main per let besser aufgehoben.

Ansonsten sieht das schon recht gut aus.

[CLOJURE]
(ns aufbietung.core
(:gen-class)
(:require [clojure.xml :as xml]
[net.cgrand.enlive-html :as html]))

(defrecord Aufbietung [fahrzeugbrief anzahl-halter fin hersteller datum])

(defn get-data
(html/select
(html/html-resource (java.net.URL. url))
#{[:.tabelle5 :tr]}))

(defn columns-for-row
[[_ a b c d e &f]]
(Aufbietung.
(:content a)
(:content (:content b))
(first (:content c))
(:content (:content d))
(:content e)))

(defn -main []
(let [uri “http://www.verkehrsblatt.de/docs/aufbietungen/suche.php?Fahrzeugbriefnummer=&FahrzeugID=&Datum=&Zeitraum_von=01.08.2013&Zeitraum_bis=01.08.2013&SuchZeitraum=Suchen#liste
data (get-data uri)]
(print
(map
#(columns-for-row (:content %))
(filter
#(.startsWith (:class (:attrs %)) “zeile”)
data)))))[/CLOJURE]

Also ich würde das in etwa so machen. Wenn ich das ganze drucke, dann gibt es im Nachhinein eine schöne Nullpointer Exception. Keine Ahnung woher. Und so würde ich es in etwa formatieren.

Hi,

danke für die Anregungen! Sehe gerade, dass in meinem Buch sogar ein ziemlich großes Kapitel zu deconstructuring gibt, das wird dann heute Abend mal die Lektüre. Auf den ersten Blick ein schönes Feature.
Das mit der schließenden Klammer ist wohl noch eine Java-Marotte, ich sehe mich vielleicht auch mal nach Quellen zu Code-Style um, da wird sich ja was finden lassen.

Gruß,
Tim

Nachtrag: Die NullPointerException fliegt wohl, weil mittendrin Zeilen stehen, welche das Attribut “class” nicht besitzen, da kann ich natürlich nicht auf startsWith prüfen, das muss ich noch behandeln.