【发布时间】:2011-09-16 00:26:38
【问题描述】:
我正在使用PHP DOM,我正在尝试在具有给定类名的 DOM 节点中获取一个元素。获取该子元素的最佳方法是什么?
更新:我最终为 PHP 使用了 Mechanize,这更容易使用。
【问题讨论】:
我正在使用PHP DOM,我正在尝试在具有给定类名的 DOM 节点中获取一个元素。获取该子元素的最佳方法是什么?
更新:我最终为 PHP 使用了 Mechanize,这更容易使用。
【问题讨论】:
更新:*[@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')]");
如果它只是一种类型的元素,您可以将 * 替换为特定的标记名。
如果你需要用非常复杂的选择器做很多事情,我会推荐Zend_Dom_Query,它支持 CSS 选择器语法(a la jQuery):
$finder = new Zend_Dom_Query($html);
$classname = 'my-class';
$nodes = $finder->query("*[class~=\"$classname\"]");
【讨论】:
my-class2 类,但非常可爱。有什么办法只选择第一个元素?
class 可以有多个类,例如:<a class="my-link link-button nav-item">。
//*[contains(concat(' ', normalize-space(@class), ' '), ' classname ')](信息量很大:CSS Selectors And XPath Expressions)。
contains 和concat...我们只是在讨论在你正在搜索的类的两边填充空格的细节,或者只是填充一侧。两者都应该工作。
如果你想在没有 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;
}
【讨论】:
$classResult = getElementByClass($dom, 'div', 'm-signature-pad'); $classResult->nodeValue = ''; $enode = $dom->createElement('img'); $enode->setAttribute('src', $signatureImage); $classResult->appendChild($enode);
还有另一种方法不使用DomXPath 或Zend_Dom_Query。
在dav原有函数的基础上,我写了如下函数,返回父节点的tag和class匹配参数的所有子节点。
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。他们的图书馆很不错。
示例:
$browser = new HttpBrowser(HttpClient::create());
$crawler = $browser->request('GET', 'example.com');
$class = $crawler->filter('.class')->first();
【讨论】:
PHP 的原生 DOM 处理非常糟糕,请帮自己一个忙,使用这个或任何其他现代 HTML 解析包,它可以在几行内处理这个问题:
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's homepage 上找到有关如何安装 Composer 的信息。
【讨论】: