/ Open Source

SQL Dialekt für Website Scraping

Schon des Öfteren musste ich Daten aus Websites extrahieren, da keine API zur Verfügung stand oder die Entwicklung einer API zu viel Zeit in Anspruch genommen hätte. Bisher habe ich das immer für jedes Projekt speziell entwickelt.
Dabei habe ich meist auf JSoup gesetzt, das es ermöglicht, unter Java mithilfe von Selektoren auf die einzelnen DOM-Elemente zuzugreifen. Hat sich nach dem Release die HTML-Stuktur der Seite geändert, musste der JSoup Code dementsprechend modifiziert werden. Hat man den Code gut strukturiert, war das nicht weiter aufwendig, aber dennoch nervig.

Vor einigen Wochen fasste ich den Entschluss, mein schon länger währendes Interesse an Antlr mit diesem Problem zu verbinden und eine SQL-Ähnliche Abfragesprache für Webseiten zu entwickeln.

Darum habe ich das Opensource Projekt ScrapeQL gestartet.

Beispiel

Nehmen wir an, ich will die Wikipedia Seite über Java parsen und die Tabelle rechts auf der Seite für mein Programm verarbeitbar machen. Dafür bastle ich mir folgende Query:

LOAD "http://en.wikipedia.org/wiki/Java_(programming_language)";
SELECT 'table.infobox.vevent' AS info_table_element FROM root;
SELECT EVERY 
  'th' AS info, 
  'td' AS description 
  IN 'tr:has(th[scope=row])' 
  FROM info_table_element 
  INTO info_table;
OUTPUT info_table;

Nun kann ich das ScrapeQL Command Line Interface starten:

java -jar ScrapeQL-cli-dist.jar < wikipedia.scrql

Die Abfrage wird verarbeitet und ich bekomme meine Antwort als JSON:

{
  "info_table" : [ {
    "info" : "Paradigm",
    "description" : "multi-paradigm: object-oriented, structured, imperative, functional, generic, reflective, concurrent"
  }, {
    "info" : "Designed by",
    "description" : "James Gosling and Sun Microsystems"
  }, {
    "info" : "Developer",
    "description" : "Oracle Corporation"
  }, {
    "info" : "Appeared in",
    "description" : "1995; 20 years ago (1995)[1]"
  }, {
    "info" : "Stable release",
    "description" : "Java Standard Edition 8 Update 31 (1.8.0_31) / January 20, 2015; 12 days ago (2015-01-20)"
  }, {
    "info" : "Preview release",
    "description" : "Java Standard Edition 9 Early Access b46 (1.9.0-ea-b46) / January 20, 2015; 12 days ago (2015-01-20)"
  }, {
    "info" : "Typing discipline",
    "description" : "Static, strong, safe, nominative, manifest"
   }, {
    "info" : "Implementation language",
    "description" : "C and C++"
  }, {
    "info" : "OS",
    "description" : "Cross-platform (multi-platform)"
  }, {
    "info" : "License",
    "description" : "GNU General Public License, Java Community Process"
  }, {
    "info" : "Filename extensions",
    "description" : ".java , .class, .jar"
  }, {
    "info" : "Website",
    "description" : "For Java Developers"
  } ]
}

JDBC

Da es sich um eine SQL basierte Javaanwendung handelt, und aus Interesse habe ich gleich einen JDBC Treiber entwickelt, der sogar Prepared Statements unterstützt. So kann man zum Beispiel das Spring JDBC Template verwenden, um aus der Query ein Java-Objekt zu erstellen.

Zukunft

Ich bin mit der Syntax noch überhaupt nicht zufrieden, ich sehe sie eher als Proof of Concept. Deshalb arbeite ich an Version 0.2, die eher an SQL angelehnt ist. Die Selektor Syntax ist sehr an JQuery orientiert.

SELECT master.$('selector') AS alias1, 
    child.$('otherselector') AS alias2, 
    external.$('h1') AS title
FROM 'test.html'.$('base_selector') master
JOIN master.$('subselect') child
JOIN PARAM_URL('single-{?}.html', ATTR(alias2,'data-id')) external;

Vision

Meine Vision für das Projekt ist, ein universelles Tool für auf HTTP aufbauende Abfragen. So könnte es mit einer einzelnen Query möglich sein, JSON von einer API mit HTML basierten Informationen aus anderer Quelle zu verbinden und ein einzelnes Objekt zu erhalten.

Weitere Informationen

Weitere Infos gibts im Wiki meines Github Projekts.