通过类名获取 DOM 元素

我正在使用 PHP DOM,并尝试在 DOM 节点中获取具有给定类名的元素。获得子元素的最佳方法是什么?

更新: 我最终使用的是 Mechanize for PHP,它更容易使用。

224072 次浏览

更新: *[@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')]");

如果只有一种类型的元素,那么可以用特定的标签名替换 *

如果你需要用非常复杂的选择器做很多这样的事情,我建议使用支持 CSS 选择器语法的 Zend_Dom_Query(类似于 jQuery) :

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

如果您希望在不使用 zend 的情况下获取类的 innerhtml,可以使用以下命令:

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

我认为被接受的方式更好,但我想这可能也有用

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

还有一种不使用 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;
}

假设你有一个变量 $html如下 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>

使用 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".

DOMDocument 输入速度很慢,而且 PhpQuery存在严重的内存泄漏问题:

Https://github.com/wasinger/htmlpagedom

选择一个类:

include 'includes/simple_html_dom.php';


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

我希望这也能帮到其他人

我更喜欢用 Symfony,他们的库很不错。

使用 DomCrawler 组件

例如:

$browser = new HttpBrowser(HttpClient::create());
$crawler = $browser->request('GET', 'example.com');
$class = $crawler->filter('.class')->first();

PHP 的原生 DOM 处理非常糟糕,帮你自己一个忙,使用这个或其他现代 HTML 解析包,它们可以在几行内处理这个问题:

安装 Paquettg/php-html-parser

composer require paquettg/php-html-parser

然后在同一文件夹中创建一个包含此内容的.php 文件

<?php


// load dependencies via Composer
require __DIR__ . '/vendor/autoload.php';


use PHPHtmlParser\Dom;


$dom = new Dom;
$dom->loadFromUrl("https://example.com");
$links = $dom->find('.classname a');


foreach ($links as $link) {
echo $link->getAttribute('href');
}

附注: 你可以找到关于如何在 作曲家网页上安装 Composer 的信息。