gpt4 book ai didi

PHPDom 遍历文档并删除没有 XPath 的节点

转载 作者:行者123 更新时间:2023-12-03 17:03:16 24 4
gpt4 key购买 nike

我正在尝试遍历文档,并删除节点(在我的情况下为所有 div),但没有 xpath(我已经可以使用 xpath 执行此操作)。出于某种原因,只有第一个 div 被删除。有小费吗?

<?php

//my totally random html
$html = '<p> Great <div> dont want this</div> </p><p> some more</p><div>more crap here</div>';

$doc = new DOMDocument();
$doc->loadHTML($html);

iterate_children($doc );
print $doc->saveHTML();


function iterate_children(&$object){
//print_r($object);

if ($object->tagName == "div") {
$object->parentNode->removeChild($object);
iterate_children($object->parentNode);
}
else {
//if($object->hasChildNodes()) {
foreach($object->childNodes as $child) {
//
iterate_children($child);
//}
}
}
}

?>

最佳答案

只有第一个 div 被删除的原因可能最简单的解释是这样的:

您遍历所有子节点。此迭代首先将当前节点设置为第一个子节点 ( DOMNode::$firstChild )。然后你处理那个 child ,完成后你继续下一个 child (即 DOMNode::$nextSibling )。

但是如果你现在从父节点中移除当前节点

$object->parentNode->removeChild($object);

迭代中的当前节点不再有任何下一个兄弟节点(因为它已从其父节点中删除)。因此,foreach 迭代在您删除第一个 div 元素后立即结束。

有不同的方法来解决这个问题。使用纯 PHP 并且不使用任何 xpath,您可以先将所有要删除的节点存储在一个数组中,然后再删除它们。函数 iterator_to_array 在这种情况下非常方便:
$divs = iterator_to_array($doc->getElementsByTagName('div'));
foreach ($divs as $div) {
$div->parentNode->removeChild($div);
}

这四行代码确实替换了您(不工作)函数(!)的所有迭代和递归逻辑。

您也可以使用 CachingIterator 来修复您的功能。当您迭代当前元素时,它在内部已经有下一个元素(当前元素被缓存)。然后它不会失效,因为从父节点中删除当前节点的那一刻,下一个节点已经被获取。

粗略地为您的代码将更改以下几行:
foreach($object->childNodes as $child) {            
iterate_children($child);
}

到:
$children = $object->childNodes;
$children = new IteratorIterator($children);
$children = new CachingIterator($children, CachingIterator::TOSTRING_USE_KEY);
foreach ($children as $child) {
iterate_children($child);
}

但请注意,此代码仅用于演示目的。如果您将其复制并粘贴到您的示例中,它会崩溃,因为您的代码中存在一些其他问题,这些问题会因此类更改而变得严重。

此代码仍然具有实际上不必要的递归,因为您可以按文档顺序迭代节点。为此,我有一个 DOMNodeIterator Iterator Garden .该库还有一个简单的 DOMElementFilter development branch .由于下一个兄弟的问题在这里是相同的,因此使用这两个需要 缓存迭代器 同样:
$divs = new CachingIterator(new DOMElementFilter(new DOMNodeIterator($doc), 'div'), CachingIterator::TOSTRING_USE_KEY);
foreach ($divs as $div) {
$div->parentNode->removeChild($div);
}

此代码再次与 iterator_to_array 非常相似。例子。由于它们的装饰性,迭代器通常使您能够创建更多可重用的代码。

我希望这可以帮助您理解为什么会发生这种情况,并且还显示了一些处理方法。

出于完整性原因,这里的代码具有更好的错误处理和遍历逻辑:
function iterate_children(DOMNode $node)
{
if ($node instanceof DOMElement and $node->tagName == "div") {
$parent = $node->parentNode;
$parent->removeChild($node);
return;
}

$children = $node->childNodes;
if (!$children) {
return;
}

$children = new IteratorIterator($children);
$children = new CachingIterator($children, CachingIterator::TOSTRING_USE_KEY);
foreach ($children as $child) {
iterate_children_old($child);
}
}

这里没有递归和数组的实现:
<?php
/**
* PHPDom iterate through document and remove nodes without XPath
*/

/my totally random html
$html = '<p> Great <div> dont want this</div> </p><p> some more</p><div>more crap here</div>';

$doc = new DOMDocument();
$doc->recover = true;
$saved = libxml_use_internal_errors(true);
$doc->loadHTML($html);
libxml_use_internal_errors($saved);

$divs = iterator_to_array($doc->getElementsByTagName('div'));
foreach ($divs as $div) {
$div->parentNode->removeChild($div);
}

echo $doc->saveHTML();

关于PHPDom 遍历文档并删除没有 XPath 的节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29757037/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com