Tôi đang sử dụng PHP DOM và tôi đang cố lấy một phần tử trong một nút DOM có tên lớp đã cho. Cách tốt nhất để có được yếu tố phụ đó là gì?
Cập nhật: Tôi đã kết thúc việc sử dụng Mechanize
cho PHP, công việc dễ dàng hơn nhiều.
Tôi đang sử dụng PHP DOM và tôi đang cố lấy một phần tử trong một nút DOM có tên lớp đã cho. Cách tốt nhất để có được yếu tố phụ đó là gì?
Cập nhật: Tôi đã kết thúc việc sử dụng Mechanize
cho PHP, công việc dễ dàng hơn nhiều.
Câu trả lời:
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 class
thuộ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_Query
hỗ 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\"]");
my-class2
là 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ố?
class
có thể có nhiều hơn một lớp chẳng hạn : <a class="my-link link-button nav-item">
.
//*[contains(concat(' ', normalize-space(@class), ' '), ' classname ')]
(Rất nhiều thông tin: Bộ chọn CSS và biểu thức XPath ).
contains
kế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ù.
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;
$classname = 'main-article'
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;
}
$classResult = getElementByClass($dom, 'div', 'm-signature-pad'); $classResult->nodeValue = ''; $enode = $dom->createElement('img'); $enode->setAttribute('src', $signatureImage); $classResult->appendChild($enode);
Ngoài ra còn có một cách tiếp cận khác mà không sử dụng DomXPath
hoặ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 $html
HTML 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".
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