136 votes

Récupérer les éléments du 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é. Quelle est la meilleure façon d'obtenir ce sous-élément ?

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

2 votes

170voto

prodigitalson Points 38549

*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, j'ai été curieux et j'ai regardé dans le code derrière Zend_Dom_Query . Il semble que le sélecteur ci-dessus soit 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 le class de sorte que même une seule classe soit délimitée par des espaces et que la liste complète des classes soit délimitée par des espaces. Ensuite, ajoutez un espace à la classe que nous recherchons. De cette façon, nous recherchons et trouvons effectivement uniquement des instances de my-class .


Utiliser un sélecteur 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 la balise * avec le tagname particulier.

Si vous avez besoin de faire beaucoup de choses de ce genre avec un sélecteur très complexe, je recommande Zend_Dom_Query qui prend en charge la syntaxe des sélecteurs CSS (à la jQuery) :

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

0 votes

Trouve la classe my-class2 aussi, mais plutôt sympa. Y a-t-il un moyen de ne choisir que le premier de tous les éléments ?

0 votes

Je ne pense pas que vous puissiez le faire sans xpath2... Cependant l'exemple de Zend_Dom_Query fait exactement cela. Si vous ne voulez pas utiliser ce composant dans votre projet, vous pourriez vouloir voir comment ils traduisent ce sélecteur css en xpath. Peut-être que DomXPath supporte xpath 2.0 - je n'en suis pas sûr.

0 votes

@prodigitalson : Merci beaucoup pour votre réponse. Je suis allé me former à XPath et j'avais une question Pourquoi utiliser contains, plutôt que de simplement faire [@class="$classname"] ?

23voto

Michael Dibbets Points 2844

Si vous souhaitez obtenir l'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;

15voto

dav Points 1069

Je pense que la manière acceptée est meilleure, mais je suppose que ceci pourrait fonctionner aussi bien.

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

11voto

user2070775 Points 523

Il existe également une autre approche sans l'utilisation de DomXPath o Zend_Dom_Query .

Sur la base de la fonction originale de dav, j'ai écrit la fonction suivante qui renvoie tous les enfants du noeud parent dont la balise 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 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 ça :

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

10voto

iautomation Points 840

DOMDocument est lent à taper et phpQuery a de gros 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 aidera quelqu'un d'autre

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X