[Vorige Seite...]
Mehr Power mit XPath!
Ich hab's gerade eben erst angesprochen - XPath ist eine feine Sache. Man kann über kompakte Queries sich eine Menge an Elemenen aus einem XML-Baum rausholen. Da HTML ein enger Verwandter ist, geht das natürlich auch mit dem DOM-Baum der aktuellen Seite und damit eröffnen sich großartige, kompakte und saubere Möglichkeiten, an Informationen aus der aktuellen Seite heranzukommen.
Als Greasemonkey-Entwickler wird man nämlich recht oft den Fall vorfinden, daß ein Element, das man manipulieren möchte, nicht mit einer (einmaligen) ID versehen ist, wodurch man dann über ein document.getElementById direkt herankommen könnte. Nein, die Realität ist da an sich viel trauriger. Man muß sich die richtige Stelle oftmals über die Seitenstruktur erschließen. Und genau da hilft auch XPath, den richtigen Knoten zu finden!
Crashkurs in XPath
Zunächsteinmal ein trauriges Beispiel anhand der Google-Seite. Die Suchergebnisse werden von der HTML-Struktur folgendermaßen aufgelistet:
CODE:
<li class="g">
< h3 class="r">Hier kommt der Klick-Titel</ h3>
<div class="s">Hier kommt die Kurzbeschreibung</div>
</li>
usw...
Um die Menge aller Klick-Titel (Links auf die Sucherergebnisse) zu erhalten, damit man zum Beispiel nen Button reinkleben kann, geht man am besten wie folgt vor:
var nodes = document.evaluate('//h3[@class="r"]', document, null, XPathResult.ANY_TYPE, null);
var h3;
while(h3 = nodes.iterateNext()) {
// Wir geben die Titel einfach mal als Alert-Kisten aus...
alert(h3.innerHTML);
}
Man richte das Augenmerk auf die Funktion document.evaluate welche man sich unbedingt auf den Mozilla-Doku-Seiten genau anschauen sollte, um die ganzen Parameter zu verstehen.
Die wichtigsten Parameter sind die ersten beiden. Der erste ist der XPath-Ausdruck. Der zweite ist der Kontextknoten, auf den sich der Ausdruck bezieht. Das wird verständlicher, wenn man sich etwas eingehender mit XPath beschäftigt hat. Ne super Tutorial-Seite über XPath befindet sich auf w3schools.com.
Grob gesagt sind XPath-Ausdrücke vergleichbar mit Dateipfaden und drücken den Weg vom Wurzelknoten zu den gewünschten Zielknoten aus.
Dieses Beispiel liefert alle Paragraph-Elemente zurück, die unmittelbare Kinder von body sind:
CODE:
/html/body/p
Wenn man jetzt alle Paragraph-Elemente auf jeglicher Baumtiefe haben möchte, so muß man Folgendes schreiben:
CODE:
//p
Mit den eckigen Klammern formuliert man einschränkende Bedingungen auf den Knoten bezogen:
CODE:
//p[@class="style_x"]
Das @class bezieht sich auf das class-Attribut der p-Knoten. Das Gleichheitszeichen ist ein Vergleichsoperator. "style_x" ist also der CSS-Klassenname, den die zu findenden Paragraph-Elemente haben sollen.
Ziemlich einfach, was? Und man kann noch abgedrehtere Sachen in der Navigation der Baumstruktur machen:
CODE:
<div>
<p>Diese Stelle will ich erreichen</p>
<span>Diese Stelle ist uninteressant</span>
<div class="xyz">Diese Stelle ist markant!</div>
</div>
Oft genug kommt es vor, daß man eine Stelle im DOM-Baum ansteuern will, die über keine markanten Features verfügt. Kurz darauf kommt allerdings etwas Eindeutiges, was man adressieren kann, wie das obige Beispiel mit dem class-Attribut "xyz" zeigt.
Um den Paragraph-Knoten zu erhalten, geht man via XPath wie folgt vor:
CODE:
//div[@class="xyz"]/preceding-sibling::p
preceding-sibling ist eine sogenannte Achse, mit der man wunderbar durchs Dokument navigieren kann.
Wie man sieht, sind sehr mächtige Suchanfragen mit XPath möglich.
Warum geht das Suchergebnis kaputt?!
Das Ergebnisobjekt von document.evaluate hat einen Haken. Sobald man im DOM-Baum auch nur die pupseligst kleinste Änderung vornimmt, ist das Objekt hinfällig und jeglicher Zugriff darauf wird mit einer Exception quittiert.
Aber nicht weinen! Es gibt einen einfachen Ausweg. Man muß einfach die ganzen gefundenen Elemente aus dem Resultat erst einmal in einem Array zwischenspeichern, bevor man was verändert:
var nodes = document.evaluate('//h3[@class="r"]', document, null, XPathResult.ANY_TYPE, null);
var h3;
var cache = new Array;
// Erst sichern
while(h3 = nodes.iterateNext()) {
cache.push(h3);
}
// Dann verändern
while(h3 = cache.pop()) {
h3.style.fontSize = '100px';
}
Nur solange die Website sich nicht ändert
Jaja... das Rumgefummle in den Eingeweiden der Homepages hat natürlich einen Haken. Man muß jeglichen gravierenden Layout-Änderungen der Ziel-Website hinterherhetzen und entsprechend die ganzen Skripte aktualisieren.
Das ist zwar doof, geht aber halt nicht anders
Sicherheitslöcher durch Benutzerskripte
Das Installieren anderer Leuts Benutzerskripte ist für einen Nicht-Programmierer eine Vertrauenssache. Die oben gezeigten Beispiele zeigen deutlich auf, daß Mißbrauch ohne weiteres möglich ist.
Mit Hilfe eines Benutzerskripts kann man sich beispielsweise Keylogger für Facebook und andere populäre Seiten bauen und über die GM_xmlhttpRequest-Funktion die geklauten Anmeldedaten an einen eigenen Server übermitteln. Cross-Site-Skripting möglich gemacht!
Um eine mögliche Panikmache zu entschärfen: alle Benutzerskripte, die man sich so runterladen und installieren kann, sind Open Source im Sinne von: ich kann den Quellcode lesen. Man braucht da einfach nur nach Vorkommnissen von GM_xmlhttpRequest zu suchen und nachvollziehen was das macht, wenn es Grund zu Zweifeln gibt.
Da die Community auf userscripts.org recht aktiv ist, schätze ich mal, daß schwarze Schafe recht flott ausfindig gemacht werden.
Also viel Spaß wünsche ich mit Greasemonkey und der Entwicklung eigener Benutzerskripte!