Daten aus HTML-Dokumenten extrahieren mit Zend_Dom_Query

Steht man vor dem Problem Daten aus HTML-Dokumenten zu extrahieren, kommen einem oft als erstes reguläre Ausdrücke in den Sinn. Kore Nordmann hat jedoch bewiesen, dass es nicht möglich ist, HTML mit regulären Ausdrücken zu parsen und empfiehlt stattdessen die DOM-Extension zu nutzen.

Genau diese nutzt die Zend_Dom_Query-Komponente und bietet eine einfache Möglichkeit, auf ein (X)HTML-Dokument zuzugreifen. Neben dem Zugriff über XPath bietet die Komponente aber auch die Möglichkeit über CSS-Selektoren bestimmte Knoten aus einem Dokument zu selektieren (wie man es von vielen Javascript-Frameworks her kennt).

Der Konstruktor erwartet einen (X)HTML- oder XML-String. Abfragen mit CSS-Selektoren werden über die query-Methode gemacht welche dann ein Zend_Dom_Query_Result-Objekt zurückliefert.

$dom = new Zend_Dom_Query($html);
$result = $dom->query('selector');
 
// Zend_Dom_Query_Result implements the Countable and Iterator interface
$count = count($result);
foreach ($result as $node) {
    // $node instanceof DOMElement
}
Folgende Elemente können bei CSS-Selektoren verwendet werden
#id

Findet das Element mit dem angegebenen id-Attribut. Zum Beispiel alle Elemente mit der id “foo” (z. B. <div id="foo"></div>):

$result = $dom->query('#foo');
element

Findet alle Elemente mit dem angegebenen tag-Namen. Zum Beispiel alle div-Elemente:

$result = $dom->query('div');
.class

Findet alle Elemente mit der angegebenen Klasse. Zum Beispiel alle Elemente mit der Klasse “foo” (z. B. <div class="foo"></div>):

$result = $dom->query('.foo');
[attribute]

Findet alle Elemente mit dem angegebenen Attribut. Dabei werden 3 Arten unterstützt.

1. Findet alle div-Elemente mit dem Attribut “foo” und dem Wert “bar” (z. B. <div foo="bar"></div>):

$result = $dom->query('div[foo="bar"]');

2. Findet alle div-Elemente mit dem Attribut “foo” dessen Wert das Wort “bar” enthält (z. B. <div foo="bar baz"></div>):

$result = $dom->query('div[foo~="bar"]');

3. Findet alle div-Elemente mit dem Attribut “foo” dessen Wert den String “bar” irgendwo enthält (z. B. <div foo="barbaz"></div>):

$result = $dom->query('div[foo*="bar"]');

Selektoren können auch “verkettet” werden um so Abhängigkeiten definieren.

Das folgende Beispiel findet alle a-Elemente die ein Unterelement eines Elements mit der Klasse “bar”
sind, welches wiederum ein Unterelement eines Elements mit der id “foo” sein muss
(z. B. <div id="foo"><div><div class="bar"><p><a href="#">Link</a></p></div></div></div>):

$result = $dom->query('#foo .bar a');

Mit einem > können direkte Abhängigkeiten definiert werden.

Das folgende Beispiel findet alle a-Elemente die ein direktes Kindelement des Elements mit der
id “foo” sind (z. B. <div id="foo"><a href="#">Link</a></div>):

$result = $dom->query('#foo > a');

Zend_Dom_Query ist übrigens auch fest in Zend_Test_PHPUnit_Controller_TestCase integriert und bietet somit CSS Selector Assertions:

<?php
class FooControllerTest extends Zend_Test_PHPUnit_Controller_TestCase
{
    public function testIndexContainsLoginForm()
    {
        $this->dispatch('/');
        $this->assertQuery('#login-form');
    }
}

Links zum Thema