Ответ 1
HTML
lxml
Элементы HTML имеют метод drop_tag()
, который вы можете вызвать для любого элемента в дереве, обработанном lxml.html
.
Он действует подобно strip_tags
тем, что он удаляет элемент, но сохраняет текст, и его можно вызвать на элементе - это означает, что вы можете легко выбрать элементы, которые вам не интересны, с помощью XPath, а затем перебирать их и удалять:
doc.html
<html>
<body>
<div>This is some <span attr="foo">Text</span>.</div>
<div>Some <span>more</span> text.</div>
<div>Yet another line <span attr="bar">of</span> text.</div>
<div>This span will get <span attr="foo">removed</span> as well.</div>
<div>Nested elements <span attr="foo">will <b>be</b> left</span> alone.</div>
<div>Unless <span attr="foo">they <span attr="foo">also</span> match</span>.</div>
</body>
</html>
strip.py
from lxml import etree
from lxml import html
doc = html.parse(open('doc.html'))
spans_with_attrs = doc.xpath("//span[@attr='foo']")
for span in spans_with_attrs:
span.drop_tag()
print etree.tostring(doc)
Вывод:
<html>
<body>
<div>This is some Text.</div>
<div>Some <span>more</span> text.</div>
<div>Yet another line <span attr="bar">of</span> text.</div>
<div>This span will get removed as well.</div>
<div>Nested elements will <b>be</b> left alone.</div>
<div>Unless they also match.</div>
</body>
</html>
В этом случае выражение XPath //span[@attr='foo']
выбирает все элементы span
с атрибутом attr
значения foo
. См. Этот учебник XPath для получения более подробной информации о том, как создавать выражения XPath.
XML/XHTML
Изменить. Я просто заметил, что вы конкретно упоминаете XHTML в своем вопросе, который в соответствии с документами лучше анализируется как XML. К сожалению, метод drop_tag()
действительно доступен только для элементов в документе HTML.
Итак, для XML это немного сложнее:
doc.xml
<document>
<node>This is <span>some</span> text.</node>
<node>Only this <span attr="foo">first <b>span</b></span> should <span>be</span> removed.</node>
</document>
strip.py
from lxml import etree
def strip_nodes(nodes):
for node in nodes:
text_content = node.xpath('string()')
# Include tail in full_text because it will be removed with the node
full_text = text_content + (node.tail or '')
parent = node.getparent()
prev = node.getprevious()
if prev:
# There is a previous node, append text to its tail
prev.tail += full_text
else:
# It the first node in <parent/>, append to parent text
parent.text = (parent.text or '') + full_text
parent.remove(node)
doc = etree.parse(open('doc.xml'))
nodes = doc.xpath("//span[@attr='foo']")
strip_nodes(nodes)
print etree.tostring(doc)
Вывод:
<document>
<node>This is <span>some</span> text.</node>
<node>Only this first span should <span>be</span> removed.</node>
</document>
Как вы можете видеть, это заменит node и все его дочерние элементы рекурсивным текстовым контентом. Я действительно надеюсь, что вы хотите, иначе все станет еще сложнее: -)
ПРИМЕЧАНИЕ Последнее изменение изменило данный код.