Recupero degli elementi DOM in base al nome della classe


Risposte:


154

Aggiornamento: versione Xpath del *[@class~='my-class']selettore css

Quindi, dopo il mio commento qui sotto in risposta al commento di hakre, mi sono incuriosito e ho esaminato il codice dietro Zend_Dom_Query. Sembra che il selettore sopra sia compilato nel seguente xpath (non testato):

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

quindi il php sarebbe:

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

Fondamentalmente, tutto ciò che facciamo qui è normalizzare l' classattributo in modo che anche una singola classe sia delimitata da spazi e l'elenco completo delle classi sia limitato da spazi. Quindi aggiungi la classe che stiamo cercando con uno spazio. In questo modo cerchiamo e troviamo effettivamente solo istanze di my-class.


Utilizzare un selettore xpath?

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

Se si tratta di un solo tipo di elemento puoi sostituire il *con il particolare tagname.

Se hai bisogno di fare molto di questo con un selettore molto complesso, consiglierei Zend_Dom_Queryche supporta la sintassi del selettore CSS (a la jQuery):

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

trova anche la classe my-class2, ma piuttosto dolce. Qualche modo per scegliere solo il primo di tutti gli elementi?
hakre

Non penso che tu possa senza xpath2 ... Tuttavia l'esempio per Zend_Dom_Query fa esattamente questo. SE non vuoi usare quel compkenet nel tuo progetto allora potresti voler vedere come stanno traducendo quel selettore CSS in xpath. Forse DomXPath supporta xpath 2.0 - non ne sono sicuro.
prodigital il

1
perché classpossono avere più di una classe, per esempio: <a class="my-link link-button nav-item">.
prodigitalil

2
@prodigitalson: Questo non è corretto in quanto non riflette gli spazi, prova //*[contains(concat(' ', normalize-space(@class), ' '), ' classname ')](molto informativo: selettori CSS ed espressioni XPath ).
hakre

1
@babonk: sì, è necessario utilizzare containsin combinazione con concat... stiamo solo discutendo i dettagli di riempire gli spazi su entrambi i lati della classe che stai cercando o riempire solo un lato. O dovrebbe funzionare comunque.
prodigitalil

20

Se desideri ottenere l'html interno della classe senza lo zend, puoi usare questo:

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

2
Punto e virgola mancante per la riga$classname = 'main-article'
Kamil

12

Penso che il modo accettato sia migliore, ma credo che anche questo potrebbe funzionare

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

2
Dov'è l'esempio per questo? Sarebbe stato carino.
robue-a7119895

È fantastico. Ho ottenuto l'elemento con la classe. Ora voglio modificare il contenuto dell'elemento, come aggiungere figlio all'elemento contenente la classe. Come aggiungere il bambino e ricreare l'intero HTML? Per favore aiuto. Questo è quello che ho fatto. $classResult = getElementByClass($dom, 'div', 'm-signature-pad'); $classResult->nodeValue = ''; $enode = $dom->createElement('img'); $enode->setAttribute('src', $signatureImage); $classResult->appendChild($enode);
Keyur

1
per la modifica dom da php penso che sia meglio usare phpquery github.com/punkave/phpQuery
dav

7

C'è anche un altro approccio senza l'uso di DomXPatho Zend_Dom_Query.

Basandomi sulla funzione originale di dav, ho scritto la seguente funzione che restituisce tutti i figli del nodo genitore il cui tag e la cui classe corrispondono ai parametri.

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

supponi di avere una variabile $htmlil seguente 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>

l'uso di getElementsByClassè semplice come:

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

6

DOMDocument è lento da digitare e phpQuery ha problemi di perdita di memoria. Ho finito per usare:

https://github.com/wasinger/htmlpagedom

Per selezionare una classe:

include 'includes/simple_html_dom.php';

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

Spero che questo aiuti anche qualcun altro

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.