Abrufen von DOM-Elementen nach Klassennamen

124

Ich verwende PHP-DOM und versuche, ein Element innerhalb eines DOM-Knotens mit einem bestimmten Klassennamen abzurufen. Was ist der beste Weg, um dieses Unterelement zu erhalten?

Update: Am Ende habe ich MechanizePHP verwendet, mit dem ich viel einfacher arbeiten konnte.

bgcode
quelle

Antworten:

154

Update: Xpath-Version des *[@class~='my-class']CSS-Selektors

Nach meinem Kommentar als Antwort auf den Kommentar von hakre wurde ich neugierig und schaute in den Code dahinter Zend_Dom_Query. Es sieht so aus, als ob der obige Selektor zu folgendem xpath (ungetestet) kompiliert wurde:

[contains(concat(' ', normalize-space(@class), ' '), ' my-class ')]

so wäre das php:

$dom = new DomDocument();
$dom->load($filePath);
$finder = new DomXPath($dom);
$classname="my-class";
$nodes = $finder->query("//*[contains(concat(' ', normalize-space(@class), ' '), ' $classname ')]");

Grundsätzlich normalisieren wir hier nur das classAttribut, sodass auch eine einzelne Klasse durch Leerzeichen und die gesamte Klassenliste durch Leerzeichen begrenzt wird. Fügen Sie dann die gesuchte Klasse mit einem Leerzeichen hinzu. Auf diese Weise suchen und finden wir effektiv nur Instanzen von my-class.


Xpath-Selektor verwenden?

$dom = new DomDocument();
$dom->load($filePath);
$finder = new DomXPath($dom);
$classname="my-class";
$nodes = $finder->query("//*[contains(@class, '$classname')]");

Wenn es sich immer nur um einen Elementtyp handelt, können Sie den *durch den jeweiligen Tagnamen ersetzen .

Wenn Sie viel davon mit einem sehr komplexen Selektor tun müssen, würde ich empfehlen, Zend_Dom_Queryder die CSS-Selektorsyntax (a la jQuery) unterstützt:

$finder = new Zend_Dom_Query($html);
$classname = 'my-class';
$nodes = $finder->query("*[class~=\"$classname\"]");
prodigitalson
quelle
findet die Klasse my-class2auch, aber ziemlich süß. Gibt es eine Möglichkeit, nur das erste aller Elemente auszuwählen?
hakre
Ich glaube nicht, dass Sie ohne xpath2 auskommen können ... Das Beispiel für Zend_Dom_Query macht jedoch genau das. Wenn Sie dieses Compkenet nicht in Ihrem Projekt verwenden möchten, möchten Sie möglicherweise sehen, wie sie diesen CSS-Selektor in xpath übersetzen. Vielleicht unterstützt DomXPath xpath 2.0 - da bin ich mir nicht sicher.
prodigitalson
1
weil classkann mehr als eine Klasse haben zum Beispiel : <a class="my-link link-button nav-item">.
prodigitalson
2
@prodigitalson: Dies ist falsch, da es nicht die Leerzeichen widerspiegelt. Versuchen Sie es //*[contains(concat(' ', normalize-space(@class), ' '), ' classname ')](Sehr informativ: CSS-Selektoren und XPath-Ausdrücke ).
hakre
1
@babonk: Ja, Sie müssen containsin Kombination mit concat... verwenden. Wir diskutieren nur die Einzelheiten des Auffüllens der Leerzeichen auf beiden Seiten der Klasse, nach der Sie suchen, oder nur das Auffüllen einer Seite. Beides sollte aber funktionieren.
Prodigitalson
20

Wenn Sie das Innerhtml der Klasse ohne das Zend erhalten möchten, können Sie Folgendes verwenden:

$dom = new DomDocument();
$dom->load($filePath);
$classname = 'main-article';
$finder = new DomXPath($dom);
$nodes = $finder->query("//*[contains(concat(' ', normalize-space(@class), ' '), ' $classname ')]");
$tmp_dom = new DOMDocument(); 
foreach ($nodes as $node) 
    {
    $tmp_dom->appendChild($tmp_dom->importNode($node,true));
    }
$innerHTML.=trim($tmp_dom->saveHTML()); 
echo $innerHTML;
Tschallacka
quelle
2
Fehlendes Semikolon für die Zeile$classname = 'main-article'
Kamil
12

Ich denke, der akzeptierte Weg ist besser, aber ich denke, das könnte auch funktionieren

function getElementByClass(&$parentNode, $tagName, $className, $offset = 0) {
    $response = false;

    $childNodeList = $parentNode->getElementsByTagName($tagName);
    $tagCount = 0;
    for ($i = 0; $i < $childNodeList->length; $i++) {
        $temp = $childNodeList->item($i);
        if (stripos($temp->getAttribute('class'), $className) !== false) {
            if ($tagCount == $offset) {
                $response = $temp;
                break;
            }

            $tagCount++;
        }

    }

    return $response;
}
dav
quelle
2
Wo ist das Beispiel dafür? Es wäre schön gewesen.
Robue-A7119895
Das ist großartig. Ich habe das Element mit der Klasse. Jetzt möchte ich den Inhalt des Elements bearbeiten, z. B. das untergeordnete Element an das Element anhängen, das die Klasse enthält. Wie kann man das Kind anhängen und den gesamten HTML-Code neu erstellen? Bitte helfen Sie. Das habe ich getan. $classResult = getElementByClass($dom, 'div', 'm-signature-pad'); $classResult->nodeValue = ''; $enode = $dom->createElement('img'); $enode->setAttribute('src', $signatureImage); $classResult->appendChild($enode);
Keyur
1
für dom Modifikation von php Ich denke , es ist besser zu Einsatz phpquery github.com/punkave/phpQuery
dav
7

Es gibt auch einen anderen Ansatz ohne die Verwendung von DomXPathoder Zend_Dom_Query.

Basierend auf der ursprünglichen Funktion von dav habe ich die folgende Funktion geschrieben, die alle untergeordneten Knoten des übergeordneten Knotens zurückgibt, deren Tag und Klasse mit den Parametern übereinstimmen.

function getElementsByClass(&$parentNode, $tagName, $className) {
    $nodes=array();

    $childNodeList = $parentNode->getElementsByTagName($tagName);
    for ($i = 0; $i < $childNodeList->length; $i++) {
        $temp = $childNodeList->item($i);
        if (stripos($temp->getAttribute('class'), $className) !== false) {
            $nodes[]=$temp;
        }
    }

    return $nodes;
}

Angenommen, Sie haben eine Variable mit $htmlfolgendem HTML-Code:

<html>
 <body>
  <div id="content_node">
    <p class="a">I am in the content node.</p>
    <p class="a">I am in the content node.</p>
    <p class="a">I am in the content node.</p>    
  </div>
  <div id="footer_node">
    <p class="a">I am in the footer node.</p>
  </div>
 </body>
</html>

Die Verwendung von getElementsByClassist so einfach wie:

$dom = new DOMDocument('1.0', 'utf-8');
$dom->loadHTML($html);
$content_node=$dom->getElementById("content_node");

$div_a_class_nodes=getElementsByClass($content_node, 'div', 'a');//will contain the three nodes under "content_node".
oabarca
quelle
6

DOMDocument ist langsam zu tippen und phpQuery hat Probleme mit Speicherverlusten. Am Ende habe ich verwendet:

https://github.com/wasinger/htmlpagedom

So wählen Sie eine Klasse aus:

include 'includes/simple_html_dom.php';

$doc = str_get_html($html);
$href = $doc->find('.lastPage')[0]->href;

Ich hoffe, das hilft auch jemand anderem

iautomation
quelle