【问题标题】:Getting DOM elements by classname通过类名获取 DOM 元素
【发布时间】:2011-09-16 00:26:38
【问题描述】:

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

更新:我最终为 PHP 使用了 Mechanize,这更容易使用。

【问题讨论】:

标签: php css dom


【解决方案1】:

更新:*[@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 类,但非常可爱。有什么办法只选择第一个元素?
  • 我认为没有 xpath2 是不行的……但是 Zend_Dom_Query 的例子就是这样做的。如果您不想在项目中使用该 compkenet,那么您可能想看看他们如何将该 css 选择器转换为 xpath。也许 DomXPath 支持 xpath 2.0 - 我不确定。
  • 因为class 可以有多个类,例如:<a class="my-link link-button nav-item">
  • @prodigitalson:这是不正确的,因为它不反映空格,请尝试//*[contains(concat(' ', normalize-space(@class), ' '), ' classname ')](信息量很大:CSS Selectors And XPath Expressions)。
  • @babonk: 是的,你需要结合使用containsconcat...我们只是在讨论在你正在搜索的类的两边填充空格的细节,或者只是填充一侧。两者都应该工作。
【解决方案2】:

如果你想在没有 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;

【讨论】:

    【解决方案3】:

    我认为接受的方式更好,但我想这也可以工作

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

    【讨论】:

    • 这个例子在哪里?会很好的。
    • 太好了。我得到了类的元素。现在我想编辑元素的内容,比如将子元素附加到包含类的元素中。如何附加孩子并重新创建整个 HTML?请帮忙。这就是我所做的。 $classResult = getElementByClass($dom, 'div', 'm-signature-pad'); $classResult-&gt;nodeValue = ''; $enode = $dom-&gt;createElement('img'); $enode-&gt;setAttribute('src', $signatureImage); $classResult-&gt;appendChild($enode);
    • 通过php修改dom我认为最好使用phpquery github.com/punkave/phpQuery
    【解决方案4】:

    还有另一种方法不使用DomXPathZend_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".
    

    【讨论】:

      【解决方案5】:

      DOMDocument 输入缓慢,phpQuery 存在严重的内存泄漏问题。我最终使用了:

      https://github.com/wasinger/htmlpagedom

      选择一个类:

      include 'includes/simple_html_dom.php';
      
      $doc = str_get_html($html);
      $href = $doc->find('.lastPage')[0]->href;
      

      我希望这对其他人也有帮助

      【讨论】:

      • 如此简单,如此美丽!与 PHP 的原生 DOM 处理相比,它的可用性非常好!请点赞,这是最有用的答案。
      【解决方案6】:

      我更喜欢为此使用 Symfony。他们的图书馆很不错。

      使用The DomCrawler Component

      示例:

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

      【讨论】:

      • 那些 BrowserKit 和 DomCrawler 组件之间的功能相当强大!
      【解决方案7】:

      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's homepage 上找到有关如何安装 Composer 的信息。

      【讨论】:

        猜你喜欢
        • 2017-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-22
        • 1970-01-01
        • 2019-06-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多