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.
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\"]");
_
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;
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;
}
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
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".