Lấy các phần tử DOM theo tên lớp


Câu trả lời:


154

Cập nhật: Phiên bản Xpath của *[@class~='my-class']bộ chọn css

Vì vậy, sau khi nhận xét của tôi dưới đây để phản hồi bình luận của hakre, tôi đã tò mò và xem xét mã phía sau Zend_Dom_Query. Có vẻ như bộ chọn ở trên được biên dịch thành xpath sau (chưa được kiểm tra):

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

vì vậy php sẽ là:

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

Về cơ bản, tất cả những gì chúng ta làm ở đây là chuẩn hóa classthuộc tính sao cho ngay cả một lớp duy nhất bị giới hạn bởi khoảng trắng và danh sách lớp hoàn chỉnh được giới hạn trong khoảng trắng. Sau đó nối thêm lớp chúng ta đang tìm kiếm với một khoảng trắng. Bằng cách này, chúng tôi đang tìm kiếm một cách hiệu quả và chỉ tìm thấy các trường hợp my-class.


Sử dụng bộ chọn xpath?

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

Nếu nó chỉ là một loại phần tử, bạn có thể thay thế *bằng tên thẻ cụ thể.

Nếu bạn cần thực hiện nhiều thao tác này với bộ chọn rất phức tạp, tôi sẽ khuyên bạn nên Zend_Dom_Queryhỗ trợ cú pháp bộ chọn CSS (a la jQuery):

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

tìm thấy lớp học my-class2là tốt, nhưng khá ngọt ngào. Bất kỳ cách nào để chỉ chọn đầu tiên của tất cả các yếu tố?
hakre

Tôi không nghĩ bạn có thể không có xpath2 ... Tuy nhiên, ví dụ cho Zend_Dom_Query thực hiện chính xác điều đó. NẾU bạn không muốn sử dụng compkenet đó trong dự án của mình thì bạn có thể muốn xem cách họ dịch bộ chọn css đó sang xpath. Có lẽ DomXPath hỗ trợ xpath 2.0 - tôi không chắc về điều đó.
prodigitalson

1
bởi vì classcó thể có nhiều hơn một lớp chẳng hạn : <a class="my-link link-button nav-item">.
prodigitalson

2
@prodigitalson: Điều này không chính xác vì nó không phản ánh các khoảng trắng, hãy thử //*[contains(concat(' ', normalize-space(@class), ' '), ' classname ')](Rất nhiều thông tin: Bộ chọn CSS và biểu thức XPath ).
hakre

1
@babonk: có, bạn cần sử dụng containskết hợp với concat... chúng tôi đang thảo luận về các chi tiết của việc đệm các khoảng trống ở cả hai phía của lớp bạn đang tìm kiếm hoặc chỉ đệm một bên. Hoặc là nên làm việc mặc dù.
prodigitalson

20

Nếu bạn muốn lấy phần bên trong của lớp mà không có zend, bạn có thể sử dụng cái này:

$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
Thiếu dấu chấm phẩy cho dòng$classname = 'main-article'
Kamil

12

Tôi nghĩ rằng cách được chấp nhận là tốt hơn, nhưng tôi đoán nó cũng có thể hoạt động tốt

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
Đâu là ví dụ cho điều này? Nó thật tuyệt
robue-a7119895 6/2/2015

Thật tuyệt. Tôi có yếu tố với lớp. Bây giờ tôi muốn chỉnh sửa nội dung của phần tử, như nối con vào phần tử có chứa lớp. Làm thế nào để nối con và tạo lại toàn bộ HTML? Xin vui lòng giúp đỡ. Đây là những gì tôi đã làm. $classResult = getElementByClass($dom, 'div', 'm-signature-pad'); $classResult->nodeValue = ''; $enode = $dom->createElement('img'); $enode->setAttribute('src', $signatureImage); $classResult->appendChild($enode);
Keyur

1
để sửa đổi dom bằng php Tôi nghĩ tốt hơn là sử dụng phpquery github.com/punkave/phpQuery
dav

7

Ngoài ra còn có một cách tiếp cận khác mà không sử dụng DomXPathhoặc Zend_Dom_Query.

Dựa trên hàm ban đầu của dav, tôi đã viết hàm sau trả về tất cả các phần tử con của nút cha có thẻ và lớp khớp với các tham số.

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

giả sử bạn có một biến $htmlHTML sau:

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

sử dụng getElementsByClassđơn giản như:

$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 chậm gõ và phpQuery có vấn đề rò rỉ bộ nhớ xấu. Tôi đã kết thúc bằng cách sử dụng:

https://github.com/wasinger/htmlpagedom

Để chọn một lớp:

include 'includes/simple_html_dom.php';

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

Tôi hy vọng điều này sẽ giúp người khác là tốt

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.