web-dev-qa-db-fra.com

Obtenir des éléments DOM par nom de classe

J'utilise PHP DOM et j'essaie d'obtenir un élément dans un nœud DOM qui a un nom de classe donné. Quel est le meilleur moyen d'obtenir ce sous-élément?

Mise à jour: J'ai fini par utiliser Mechanize pour PHP qui était beaucoup plus facile à utiliser.

117
bgcode

Mise à jour: version Xpath de _*[@class~='my-class']_ sélecteur css

Donc, après mon commentaire ci-dessous en réponse au commentaire de hakre, je suis devenu curieux et j'ai jeté un œil dans le code derrière _Zend_Dom_Query_. Il semble que le sélecteur ci-dessus est compilé dans le xpath suivant (non testé):

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

donc le php serait:

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

Fondamentalement, tout ce que nous faisons ici est de normaliser l’attribut class de sorte que même une seule classe soit limitée par des espaces et que la liste complète des classes soit limitée par des espaces. Ajoutez ensuite la classe que vous recherchez avec un espace. De cette manière, nous recherchons et ne trouvons que des instances de _my-class_.


Utilisez un sélecteur de xpath?

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

S'il ne s'agit que d'un seul type d'élément, vous pouvez remplacer le _*_ par la variable particulière.

Si vous avez besoin de faire beaucoup de ceci avec un sélecteur très complexe, je vous recommanderais Zend_Dom_Query qui supporte la syntaxe de sélecteur CSS (à la manière de jQuery):

_$Finder = new Zend_Dom_Query($html);
$classname = 'my-class';
$nodes = $Finder->query("*[class~=\"$classname\"]");
_
146
prodigitalson

Si vous souhaitez obtenir le innerhtml de la classe sans le zend, vous pouvez utiliser ceci:

$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;
17
Tschallacka

Je pense que la méthode acceptée est meilleure, mais je suppose que cela pourrait également fonctionner

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

DOMDocument est lent à taper et phpQuery présente des problèmes de fuite de mémoire. J'ai fini par utiliser:

https://github.com/wasinger/htmlpagedom

Pour sélectionner une classe:

include 'includes/simple_html_dom.php';

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

J'espère que cela aide aussi quelqu'un d'autre

5
iautomation

Il existe également une autre approche sans utiliser DomXPath ou Zend_Dom_Query.

Basé sur la fonction d'origine de dav, j'ai écrit la fonction suivante qui renvoie tous les enfants du nœud parent dont l'étiquette et la classe correspondent aux paramètres.

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

supposons que vous ayez une variable $html le code HTML suivant:

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

l'utilisation de getElementsByClass est aussi simple que:

$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".
5
oabarca