Obtener elementos DOM por nombre de clase


Estoy usando PHP DOM y estoy tratando de obtener un elemento dentro de un nodo DOM que tenga un nombre de clase dado. ¿Cuál es la mejor manera de obtener ese subelemento?

Update: Terminé usando Mechanize para PHP que era mucho más fácil de trabajar.

 103
Author: bgcode, 2011-06-16

5 answers

Actualización: Versión Xpath de *[@class~='my-class'] selector css

Así que después de mi comentario a continuación en respuesta al comentario de hakre, tuve curiosidad y miré el código detrás de Zend_Dom_Query. Parece que el selector anterior está compilado con el siguiente xpath (no probado):

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

Así que el php sería:

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

Básicamente, todo lo que hacemos aquí es normalizar el atributo class para que incluso una sola clase esté limitada por espacios, y la lista completa de clases esté limitada en espacio. A continuación, añadir la clase que estamos buscando con un espacio. De esta manera estamos efectivamente buscando y encontramos solo instancias de my-class.


¿Usar un selector xpath?

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

Si solo hay un tipo de elemento, puede reemplazar el * con el tagname particular.

Si necesita hacer mucho de esto con un selector muy complejo, lo recomendaría Zend_Dom_Query que soporta sintaxis de selector CSS (a la jQuery):

$finder = new Zend_Dom_Query($html);
$classname = 'my-class';
$nodes = $finder->query("*[class~=\"$classname\"]");
 126
Author: prodigitalson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-30 15:57:55

Si desea obtener el innerhtml de la clase sin el zend, puede usar esto:

$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;
 13
Author: Tschallacka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-26 07:12:36

Creo que el camino aceptado es mejor, pero supongo que esto podría funcionar también

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;
}
 9
Author: dav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-17 16:57:33

También hay otro enfoque sin el uso de DomXPath o Zend_Dom_Query.

Basado en la función original de dav, escribí la siguiente función que devuelve todos los hijos del nodo padre cuya etiqueta y clase coinciden con los parámetros.

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;
}

Supongamos que tiene una variable $html el siguiente HTML:

<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>

El uso de getElementsByClass es tan simple como:

$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".
 4
Author: user2070775,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-24 17:54:10

DOMDocument es lento para escribir y phpQuery tiene problemas de pérdida de memoria. Terminé usando:

Https://github.com/wasinger/htmlpagedom

Para seleccionar una clase:

include 'includes/simple_html_dom.php';

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

Espero que esto ayude a alguien más también

 3
Author: iautomation,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-05 23:23:26