Hackety hack hack

Atypisches Nutzerverhalten mit Rat und Tat.

Was gibt es hier zu sehen? Mediawiki APIs hacken

Chantek ist ein besonderer Orang-Utan. Im Yerkes Regional Primate Research Center geboren lernte er innerhalb eines Forschungsprojektes mittels der amerikanischen Gebärdensprache (ASL) mit Menschen zu kommunizieren und gesprochenes Englisch zu verstehen, Grundlagen des Umgangs mit Geld und Spiele. Mit fünfeinhalb Jahren wurde ihm der Stand eines etwa zweijährigen Kindes attestiert, wobei er in einigen Bereichen (z. B. Sprachverständnis und Werkzeugnutzung) die Fertigkeiten eines vierjährigen Kindes aufwies. Über Chantek gibt es in der Wikipedia noch mehr zu lesen, als ich hier zitiert und zusammengefasst habe.

Chantek ist aber auch der Name eines Hacks, der auf dem Mediawiki Hackathon 2014 in Amsterdam von Hay Kranen gebastelt wurde. Auf Niederländisch heisst “Api” auch Affe. Eine API, die menschliche Sprache (zumindest ein bisschen) versteht, ist gut geeignet, um nach dem sprechenden Affen Chantek benannt zu werden.

Über die Mediawiki API habe ich im letzten Blogbeitrag bereits eine umfassende Einführung geschrieben. Chantek vereinfacht diese API auf besonders schlanke Weise und macht die Unterhaltung ein bisschen angenehmer. Um Properties bei Wikidata abzufragen und Titel darzustellen, brauche ich bei der klassischen Mediawiki API zu Wikidata typischerweise mehrere API-Calls. Bei Chantek kann ich zum Beispiel sagen, dass mich nur Abfrageergebnisse auf Deutsch interessieren und ich bekomme die Werte dann auch mit ihrer deutschen Bezeichnung zurück.

Über die Grenzen der Same-Origin-Policy hinweg mit CORS

Chantek ist eine Python-Anwendung, die die CORS-Spezifikation implementiert, um JavaScript-Anwendungen in Web-Anwendungen zu ermöglichen, Daten auszulesen und darzustellen. Normalerweise gibt es bei JavaScript im Browser die Same-Origin-Policy, die verhindert, dass Daten aus beliebigen (und damit potenziell unsicheren) Quellen einfach so nachgeladen werden können: Clientseitiges JavaScript kann nur auf Daten der Domain zugreifen, woher auch das JavaScript selbst geladen wurde. Bei API-Calls stellt sich diese Maßnahme als gelinde gesagt schwierig dar. Um diese Beschränkung zu umgehen, gibt es zum Beispiel JSONP, das aber selbst nicht unproblematisch ist. CORS ist eine Möglichkeit, die Grenzen der Same-Origin-Policy zu überschreiten, die auch Authentifizierung und feinere Sicherheitsabstufungen beinhaltet und vom W3C spezifiziert wurde.

Leider ist CORS nicht in allen Browsern vorhanden. Wie man browserunhabhängig prüft, ob CORS zur Verfügung steht (bei den meisten modernen Browsern ist das der Fall), zeigt etwa dieses Tutorial von Monsur Hossain und der darin benutzte Code:

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) {
    // Check if the XMLHttpRequest object has a "withCredentials" property.
    // "withCredentials" only exists on XMLHTTPRequest2 objects.
    xhr.open(method, url, true);
  } else if (typeof XDomainRequest != "undefined") {
    // Otherwise, check if XDomainRequest.
    // XDomainRequest only exists in IE, and is IE's way of making CORS requests.
    xhr = new XDomainRequest();
    xhr.open(method, url);
  } else {
    // Otherwise, CORS is not supported by the browser.
    xhr = null;
  }
  return xhr;
}

Zeig mir meine Umgebung, Wikidata!

Mit Chantek (und CORS) wollte ich eine JavaScript-Webanwendung schreiben, die mir anzeigt, was es in Wikidata um mich herum an geogetaggten Wisssensgegenständen gibt. In Berlin (zumindest innerhalb des S-Bahn-Rings) ist das einiges — viele interessante Orte sinde nur ein paar Hundert Meter weit entfernt und haben einen Wikipedia-Artikel. Auf dem Land müsste ich meine Anwendung vermutlich ein bisschen erweitern: ein Radius von 500m ist nicht besonders üppig für manche Gegenden. Auch, dass ich Wissensgegenstände nur auf Deutsch suche und Artikel in der deutschsprachigen Wikipedia dazu anzeige, könnte eine überarbeitete Version meiner Anwendung als Parameter änderbar machen.

Als kleiner, schneller Hack zwischendurch, aufgespielt auf einen Webserver, hat mir meine Anwendung aber durchaus Spaß bereitet. Mit dem Mobiltelefon kann ich so schauen, was Wikidata für meine Umgebung anzeigt.

Ein API-Call, um Dinge im meiner Umgebung zu finden, kann mit Chantek etwa so aussehen:

 
http://chantek.bykr.org/wikidata/query?q=around[625,52.205,0.119,15]

Hiermit frage ich Chantek (über die WikidataQuery-API) nach Wikidata-Wissensgegenständen im Umkreis von 15km um Cambridge in England: der erste Parameter (625) bezieht sich auf den Planeten, für den die Koordinaten gelten (Erde), die nächsten zwei sind die Koordinaten von Cambridge und mit 15 gebe ich den Radius an. Auch Brüche sind bei der Angabe des Radius möglich, 0.5 ist also 500m.

Jetzt fehlen mir nur noch meine aktuellen Koordinaten. Moderne Browser unterstützen das HTML5-Feature Geolocation, mit der JavaScript-Bibliothek Modernizr kann ich überprüfen, ob dieses Feature vorhanden ist. Da es Privatsphärenbedenken bei der Abfrage von Geokoordinaten gibt, muss der Browser bei der ersten Abfrage der Koordinaten des Standorts von einer neuen Seite die Benutzerin oder den Benutzer fragen, ob das so in Ordnung ist. Wenn zugestimmt wird, können wir die Koordinaten benutzen, eine Abfrage starten und bekommen eine Liste von Wikidata-Items zurück. Tatsächlich erzeugen wir zuerst einen CORS-Request und als Callback-Funktion geht es gleich in die nächste Abfrage, denn wir wollen ja für jedes Element der Liste einen Titel und eine Wikipedia-Seite haben. Das könnte zum Beispiel so aussehen:

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function locate_me() {
    // Check if we can do HTML5 geolocation
    if (Modernizr.geolocation) {
       navigator.geolocation.getCurrentPosition(function (position) {
          var lat = position.coords.latitude;
          var lon = position.coords.longitude;
          var chantek = "http://chantek.bykr.org/wikidata/";
          // Query data around me
          var query = "query?q=around[625," + lat + "," + lon + "," + "0.5]&callback=?";
          var query_xhr = createCORSRequest('GET', chantek+query);
          if (!query_xhr) {
            throw new Error('CORS not supported');
          }
          query_xhr.onload = function(){
            var response = query_xhr.responseText;
            var data = $.parseJSON(response).response;
            $.each(data, function(index, value) {
              // Call queried entities and display them
              var entity = "entity?q=Q"+ value + "&lang=de&callback=?";
              var entity_xhr = createCORSRequest('GET', chantek+entity);
              entity_xhr.onload = function(){
                var response = entity_xhr.responseText;
                var data = $.parseJSON(response).response;
                $("#location-list").append(
                    "<li>" +
                    "<big>" +
                    "<a href=" + "\"" +
                    data[Object.keys(data)[0]].sitelinks.de.url +
                    "\">"+
                    data[Object.keys(data)[0]].labels +
                    "</a>" +
                    "</big>" +
                    "</li>"
                );
              };
              entity_xhr.onerror = function() {
                throw new Error('Error making the request');
              };
              entity_xhr.send();
            });
          };
          query_xhr.onerror = function() {
            throw new Error('Error making the request');
          };
          query_xhr.send();
    });
  } else {
      throw new Error('Geolocation not supported');
  }
}

Der fertige Hack

Mit ein bisschen UI und HTML drumherum habe ich diesen kleinen Hack unter http://www.johl.io/aroundme-wikidata/ ins Netz gestellt. Ich wünsche viel Spaß!

In der Nähe des U-Bahnhofs Eberswalder Straße in Berlin sieht das auf meinem Mobiltelefon etwa so aus:

Comments