รับองค์ประกอบ DOM ตามชื่อคลาส


124

ฉันใช้PHP DOMและฉันกำลังพยายามรับองค์ประกอบภายในโหนด DOM ที่มีชื่อคลาสที่กำหนด วิธีที่ดีที่สุดในการรับองค์ประกอบย่อยนั้นคืออะไร

อัปเดต:ฉันลงเอยด้วยการใช้MechanizePHP ซึ่งทำงานได้ง่ายกว่ามาก


คำตอบ:


155

อัปเดต: *[@class~='my-class']ตัวเลือก css เวอร์ชัน Xpath

ดังนั้นหลังจากที่ความคิดเห็นของฉันด้านล่างในการตอบสนองต่อความคิดเห็น hakre Zend_Dom_Queryผมอยากรู้อยากเห็นและมองเข้าไปในรหัสที่อยู่เบื้องหลัง ดูเหมือนว่าตัวเลือกด้านบนจะคอมไพล์กับ xpath ต่อไปนี้ (ยังไม่ทดลอง):

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

ดังนั้น php จะเป็น:

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

โดยพื้นฐานแล้วสิ่งที่เราทำที่นี่คือทำให้classแอตทริบิวต์เป็นมาตรฐานเพื่อให้แม้แต่คลาสเดียวถูกล้อมรอบด้วยช่องว่างและรายการคลาสทั้งหมดจะถูกล้อมรอบด้วยช่องว่าง จากนั้นต่อท้ายชั้นเรียนที่เรากำลังค้นหาด้วยช่องว่าง ด้วยวิธีนี้เรากำลังมองหาและพบเฉพาะกรณีของmy-class.


ใช้ตัวเลือก xpath?

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

หากเป็นองค์ประกอบเพียงประเภทเดียวคุณสามารถแทนที่*ด้วย tagname เฉพาะได้

หากคุณต้องการทำสิ่งนี้ให้มากด้วยตัวเลือกที่ซับซ้อนมากฉันขอแนะนำZend_Dom_Queryซึ่งรองรับไวยากรณ์ตัวเลือก CSS (a la jQuery):

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

เจอคลาสmy-class2เหมือนกัน แต่สวยหวาน มีวิธีใดในการเลือกเฉพาะองค์ประกอบแรกของทั้งหมด?
hakre

ฉันไม่คิดว่าคุณสามารถทำได้หากไม่มี xpath2 ... อย่างไรก็ตามตัวอย่างของ Zend_Dom_Query ทำอย่างนั้น หากคุณไม่ต้องการใช้ compkenet นั้นในโปรเจ็กต์ของคุณคุณอาจต้องการดูว่าพวกเขากำลังแปล css selector เป็น xpath อย่างไร บางที DomXPath อาจรองรับ xpath 2.0 - ฉันไม่แน่ใจเกี่ยวกับเรื่องนี้
prodigitalson

1
เนื่องจากclassสามารถมีได้มากกว่าหนึ่งคลาสเช่น<a class="my-link link-button nav-item">.
prodigitalson

2
@prodigitalson: สิ่งนี้ไม่ถูกต้องเนื่องจากไม่สะท้อนช่องว่างให้ลอง//*[contains(concat(' ', normalize-space(@class), ' '), ' classname ')](ข้อมูลมาก: CSS Selectors และนิพจน์ XPath )
hakre

1
@babonk: ใช่คุณต้องใช้containsร่วมกับconcat... เรากำลังพูดคุยเกี่ยวกับรายละเอียดของการเพิ่มช่องว่างทั้งสองด้านของชั้นเรียนที่คุณกำลังค้นหาหรือเพิ่มเพียงด้านเดียว ควรใช้อย่างใดอย่างหนึ่ง
prodigitalson

20

หากคุณต้องการรับ innerhtml ของคลาสโดยไม่มี zend คุณสามารถใช้สิ่งนี้:

$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
ไม่มีเครื่องหมายอัฒภาคสำหรับบรรทัด$classname = 'main-article'
Kamil

12

ฉันคิดว่าวิธีที่ได้รับการยอมรับนั้นดีกว่า แต่ฉันคิดว่าวิธีนี้อาจได้ผลเช่นกัน

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
ตัวอย่างนี้อยู่ที่ไหน? คงจะดีไม่น้อย
robue-a7119895

เยี่ยมมาก ฉันมีองค์ประกอบกับชั้นเรียน ตอนนี้ฉันต้องการแก้ไขเนื้อหาขององค์ประกอบเช่นต่อท้ายเด็กกับองค์ประกอบที่มีคลาส จะต่อท้ายเด็กและสร้าง HTML ใหม่ทั้งหมดได้อย่างไร กรุณาช่วย. นี่คือสิ่งที่ฉันได้ทำ $classResult = getElementByClass($dom, 'div', 'm-signature-pad'); $classResult->nodeValue = ''; $enode = $dom->createElement('img'); $enode->setAttribute('src', $signatureImage); $classResult->appendChild($enode);
Keyur

1
สำหรับการแก้ไข dom โดย php ฉันคิดว่ามันดีกว่าที่จะใช้ phpquery github.com/punkave/phpQuery
dav

7

นอกจากนี้ยังมีวิธีการอื่นโดยไม่ใช้หรือDomXPathZend_Dom_Query

จากฟังก์ชันดั้งเดิมของ dav ฉันเขียนฟังก์ชันต่อไปนี้ที่ส่งคืนลูกทั้งหมดของโหนดแม่ที่มีแท็กและคลาสตรงกับพารามิเตอร์

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

สมมติว่าคุณมีตัวแปร$htmlHTML ต่อไปนี้:

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

การใช้งานgetElementsByClassทำได้ง่ายเพียงแค่:

$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พิมพ์ช้าและphpQueryมีปัญหาหน่วยความจำรั่ว ฉันลงเอยด้วยการใช้:

https://github.com/wasinger/htmlpagedom

ในการเลือกชั้นเรียน:

include 'includes/simple_html_dom.php';

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

ฉันหวังว่านี่จะช่วยคนอื่นเช่นกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.