[PHP Grundlagen] Einführung DOMDocument / DOMXPath - Druckversion +- PHP Rocks (https://www.php-rocks.de) +-- Forum: Knowledge Base (https://www.php-rocks.de/https://www.php-rocks.de/forum/18-knowledge-base.html) +--- Forum: Tutorials (https://www.php-rocks.de/https://www.php-rocks.de/forum/20-tutorials.html) +--- Thema: [PHP Grundlagen] Einführung DOMDocument / DOMXPath (/https://www.php-rocks.de/thema/74-einfuehrung-domdocument-domxpath.html) |
Einführung DOMDocument / DOMXPath - Arne Drews - 20.09.2015 DOMDocument und DOMXPath... Äh... Was ist das denn? Mit DOMDocument steht uns ein Werkzeug zur Verfügung, HTML oder XML Dokumente zu untersuchen und auf bestimmte Elemente darin zuzugreifen.Auch die Manipulation des Dokumentes ist bspw. mit dem Erstellen und Einhängen neuer Elemente im Dokumentenbaum selbst möglich, sowie bspw. auch das Erstellen eines neuen XML-Dokumentes. DOMXPath ist ein zusätzliches, mächtiges Selektionswerkzeug, mit dem ein Dokument vom Typ DOMDocument auf Pfad-Ebene bereitgestellt wird.Auf dieser Pfadebene lassen sich per DOMXPath sog. Queries anwenden, die das Selektieren von Elementen zum Kinderspiel machen.Den Vorteil gegenüber den DOMDocument Boardmitteln möchte ich in diesem Tutorial anhand von kleinen Beispielen kurz aufzeigen.DOMDocument Ich möchte zu Beginn kurz anhand einiger kleinen Beispiele die Möglichkeiten von DOMDocument aufzeigen, um dann zu DOMXPath zu wechseln.Als Grundlage für das gesamte Tutorial verwende ich folgende Beispiel HTML-Seite ( startseite.html ):Die Seite ist nur mit für das Tutorial nützlichen Elementen gefüllt und stellt nicht unbedingt ein sinnvolles oder konformes Dokument dar! Code: <!DOCTYPE html> Wir fangen an! Zu Beginn erstellen wir eine Instanz der Klasse DOMDocument und laden unsere Beispiel HTML-Datei dort hinein:PHP-Code: $oDom = new DOMDocument; $oDom Zugriff auf den gesamten Dokumentenbaum unserer Beispiel-Datei.Aber was können wir jetzt damit anfangen? Nun, wir können bspw. Elemente selektieren und weiter verarbeiten. Elemente/Nodes selektieren und verwenden Dazu stehen uns mehrere Möglichkeiten zur Verfügung, wovon ich die folgenden gerne kurz aufzeigen möchte:
getElementById() an. Mit dieser können wir das Element selektieren, welches den angegebenen Wert in dem Attribut id besitzt.Die Methode ist also quasi ein Server basiertes Äquivalent zu der gleichnamigen JavaScript-Methode: PHP-Code: // selektieren des Elements mit id="logo" id="logo" . Die Methode DOMDocument::getElementById() liefert als Rückgabe im Erfolgsfall eine Instanz der Klasse DOMElement , über die wir auf bestimmte Eigenschaften und Methoden verfügen, um das selektierte Element weiter zu verarbeiten.Bspw. könnten wir uns nun mit der Methode DOMElement::getAttribute() den Wert der Attribute unseres Elements zurückgeben lassen, buw. über DOMElement:: setAttribute() auch Attribute neu setzen.PHP-Code: // Attribut auslesen HinweisDie KlasseDOMElement erbt Eigenschaften und Methoden der Klasse DOMNode , so daß uns bspw. die Möglichkeit zur Verfügung steht, den Inhalt unseres Elementes anzeigen zu lassen: PHP-Code: echo $oDomElement->nodeValue;
Element-/NodeList selektieren Wir gehen einen Schritt weiter und möchten jetzt bspw. alle <p> -Tags unseres Dokumentes selektieren. Diese werden bspw. über die Methode DOMDocument::getElementsByTagName() selektiert und als Instanz der Klasse DOMNodeList zur Verfügung gestellt:PHP-Code: $oDomNodeList = $oDom->getElementsByTagName( 'p' ); PHP-Code: foreach ( $oDomNodeList as $oDomNode ) { Ausgabe: irgendein Text <p> -Tag aus dem Footer ran. Wir müssten immer eine Liste von Elementen selektieren und mittels Iteration und Prüfungen unser gesuchtes Element aufspüren.Einfacher geht's mit DOMXPath! Mit DOMXPath wird diese Anforderung über eine sogenannte Abfrage ( Query ) umgesetzt. Dazu bildet DOMXPath den Dokumentenbaum als Pfad ab, in dem wir uns quasi wie in Verzeichnissen ( mit einigen Besonderheiten natürlich ) bewegen können.Fangen wir nochmal ganz vorne an, um uns eine Instanz der Klasse DOMXPath für unser Dokument zu erstellen:PHP-Code: $oDom = new DOMDocument; DOMXPath benötigt lediglich das DOMDocument -Objekt zur Initialisierung.Uns stehen jetzt alle Möglichkeiten von DOMXPath zur Verfügung. Nehmen wir doch gleich mal ein Beispiel, das den Vorteil an unserem Dokument deutlich zeigt:PHP-Code: $oDomNodeList = $oXPath->query( '//p[@class="zweiter"]' ); <p> -Tag mit der CSS-Klasse zweiter abgefragt. Wurde ein solches Element gefunden, wird dies in einer DOMNodeList zurückgeliefert. Wir könnten jetzt über das Ergebnis iterieren, genau wie wir es bei dem Beispiel von DOMDocument::getLementsByTagName() gemacht haben. Da wir in diesem Fall aber wissen, daß wir nur ein Element in der Liste haben, können wir auch direkt darauf zugreifen. Ermöglichen tut uns dies die Methode DOMNodeList::item() , die uns eine Instanz der Klasse DOMNode liefert:PHP-Code: echo $oDomNodeList->item(0)->nodeValue; Ausgabe: zweiter Absatz HinweisDOMXPath::query() unterscheidet erstmal nicht zwischen der Menge der gefunden Elemente und gibt erstmal alles als DOMNodeList zurück, auch wenn es nur ein Element beinhaltet! Über die Methode DOMNodeList::item() haben wir jedoch direkten Zugriff auf jedes einzelne Element darin.Aber so eine Abfrage sieht kompliziert aus! Eigentlich nicht, wenn man weiß, was da steht. Um das zu klären, wollen wir die grundsätzlichen Merkmale einer Query betrachten. Wir haben bereits gelernt, daß DOMXPath unser Dokument als Pfad zur Verfügung stellt. Als Pfad betrachtet, schauen wir uns mal den Weg zu den <p> -Tag Elementen des Footers an:Pfad: /html/body/div/p <p> -Tag Elemente als DMONodeList .Was wir aber wollen, ist nur das zweite Element, daher könnte man jetzt einfach hergehen und aus der DOMNodeList das zweite item extrahieren:PHP-Code: $oDomNodeList = $dXPath->query( '/html/body/div/p' ); Ausgabe: erster Absatz erster Absatz ? Was haben wir falsch gemacht?Grundsätzlich haben wir keinen Fehler gemacht. Uns war vermutlich nur nicht bewußt, daß es ja auch noch andere <p> -Tag Elemente gibt, die über genau den gleichen Pfad abgebildet werden. In unserem Fall befindet sich ein weiterer <p> -Tag in dem <div> -Tag mit der Klasse content . Und als Ergebnismenge liefert uns DOMXPath->query() alle Elemente, die es mit der Pfad-Angabe findet. Also müssen wir genauer werden, und genau da kommen die besonderen Möglichkeiten einer Query ins Spiel. Wir sagen einfach, daß wir nur die <p> -Tag Elemente haben möchten, die sich im <div> -Tag mit der CSS-Klasse footer befinden. Aber wie?Ganz einfach, dazu verwendet man unmittelbar hinter dem Tag-Bezeichner eckige Klammern, um auf dessen Attribute zugreifen zu können. Dabei sollten wir nur wissen, daß ein Attributname mit einem @ eingeleitet wird. Die Anforderung sieht damit dann nun so aus:PHP-Code: $oDomNodeList = $oXPath->query( 'html/body/div[@class="footer"]/p' ); Ausgabe: zweiter Absatz Mit dem Wissen über Attribute, das wir gerade erlernt haben, können wir das auch relativ einfach umsetzen: PHP-Code: $oDomNodeList = $oXPath->query( 'html/body/div[@class="footer"]/p[@class="zweiter"]' ); Ausgabe: zweiter Absatz item(0) ansprechen müssen.Geht das auch kürzer? Ja, das geht natürlich auch kürzer, denn DOMXPath::query() kennt mehr als nur einen Slash. Er kennt den Doppelslash!Mit Angabe zweier Slashes als Pfadtrenner sagen wir der Query, daß es vollkommen egal ist, wo wir uns im Pfad gerade befinden, er soll alles, was danach kommt einfach suchen. Klingt erstmal einfach, aber doch verwirrend. Deshalb wollen wir das direkt an unserem Beispiel zeigen. Wir wissen, unser <p> -Tag hat die CSS-Klasse zweiter . Also sagen wir der Query einfach, sie soll im gesamten Dokument nach jedem <p> -Tag mit der Klasse zweiter suchen:PHP-Code: $oDomNodeList = $oXPath->query( '//p[@class="zweiter"]' ); Ausgabe: zweiter Absatz HinweisWie in den Ausgaben zu erkennen ist, steht uns in der PropertyDOMNode::nodeValue lediglich der PlainText zur Verfügung. Möchten wir den Node als HTML haben, müssen wir diesen zunächst über die Methode DOMDocument:: saveHTML() HTML formatiert speichern. Ab PHP v5.3.6 kann man hier allerdings auch den optionalen Parameter von DOMDocument:: saveHTML([DOMNode $node]) verwenden, um den DOM direkt auszugeben. Bezogen auf unser letztes Beispiel könnte eine Ausgabe in HTML-Form so aussehen:PHP-Code: $oDomNode = $oXPath->query( '//p[@class="zweiter"]' )->item(0); DOMNode::ownerDocument liefert uns das referenzierte DOMDocument -Objekt, so daß wir das wie oben zu sehen bequem über chaining lösen können.Wäre $oDomNode jetzt bspw. eine Tabelle, hätten wir als Ausgabe diese komplett in HTML Form.DOMDocument/DOMXPath .Es gibt noch einige weitere Feinheiten zu DOMXPath zu sagen, aber das werde ich in einem weiterführenden Tutorial noch mal ausführlicher beschreiben.HinweisAlle in diesem Tutorial besprochenen Klassen und deren weitergehende Nutzung sind in der offiziellen Dokumentation nachlesbar:
|