在lxml中迭代时就地编辑树

2 投票
1 回答
1236 浏览
提问于 2025-04-16 18:28

我正在使用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>

我觉得这有点像个不太好的小技巧——有没有更合理的方法来使用这个库实现这个功能呢?

1 个回答

5

为了简化操作,你可以使用 lxml库在XPath中对正则表达式的支持,这样就可以找到并删除那些不需要的节点,而不需要一个一个去检查所有的子节点。

这样做的结果和你的脚本是一样的:

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)

撰写回答