在lxml中进行迭代时,就地编辑树

问题描述

| 我正在使用lxml解析html并对其进行编辑以生成新文档。从本质上讲,我正在尝试像javascript DOM一样使用它-我知道这并不是真正的预期用途,但是到目前为止,它的大部分工作都很好。 当前,我使用iterdescendants()获取可迭代的元素列表,然后依次处理每个元素。 但是,如果元素在迭代过程中被删除,则仍会考虑其子元素,因为删除不会像您期望的那样影响迭代。为了获得我想要的结果,此骇客作品:
from lxml.html import fromstring,tostring
import urllib2
import re

html = \'\'\'
<html>
<head>
</head>

<body>
    <div>
        <p class=\"unwanted\">This content should go</p>
        <p class=\"fine\">This content should stay</p>
    </div>

    <div id = \"second\" class=\"unwanted\">
        <p class = \"alreadydead\">This content should not be looked at</p>
        <p class = \"alreadydead\">nor should this</>
        <div class=\"alreadydead\">
            <p class=\"alreadydead\">Still dead</p>
        </div>
    </div>

    <div>
        <p class=\"yeswanted\">This content should also stay</p>
    </div>
</body>
for element in allElements:
   s = \"%s%s\" % (element.get(\'class\',\'\'),element.get(\'id\',\'\'))        
   if re.compile(\'unwanted\').search(s):
       for i in range(len(element.findall(\'.//*\'))):
           allElements.next()
       element.drop_tree()

print tostring(page.body)
输出
<body>
    <div>

        <p class=\"yeswanted\">This content should stay</p>
    </div>



    <div>
        <p class=\"yeswanted\">This content should also stay</p>
    </div>
</body>
感觉就像一个讨厌的黑客-是否有更明智的方式使用该库来实现这一目标?     

解决方法

        为简化起见,您可以在XPath中使用lxml \对正则表达式的支持来查找并杀死不需要的节点,而无需遍历所有后代。 这将产生与脚本相同的结果:
EXSLT_NS = \'http://exslt.org/regular-expressions\'
XPATH = r\"//*[re:test(@class,\'\\bunwanted\\b\') or re:test(@id,\'\\bunwanted\\b\')]\"

tree = lxml.html.fromstring(html)
for node in tree.xpath(XPATH,namespaces={\'re\': EXSLT_NS}):
    node.drop_tree()
print lxml.html.tostring(tree.body)