Necesito eliminar elementos por completo, según el contenido de un atributo, usando el lxml de python. Ejemplo:
import lxml.etree as et
xml="""
<groceries>
<fruit state="rotten">apple</fruit>
<fruit state="fresh">pear</fruit>
<fruit state="fresh">starfruit</fruit>
<fruit state="rotten">mango</fruit>
<fruit state="fresh">peach</fruit>
</groceries>
"""
tree=et.fromstring(xml)
for bad in tree.xpath("//fruit[@state=\'rotten\']"):
#remove this element from the tree
print et.tostring(tree, pretty_print=True)
Me gustaría imprimir esto:
<groceries>
<fruit state="fresh">pear</fruit>
<fruit state="fresh">starfruit</fruit>
<fruit state="fresh">peach</fruit>
</groceries>
¿Hay alguna manera de hacer esto sin almacenar una variable temporal e imprimirla manualmente, como:
newxml="<groceries>\n"
for elt in tree.xpath('//fruit[@state=\'fresh\']'):
newxml+=et.tostring(elt)
newxml+="</groceries>"
Estás buscando la
remove
función. Llame al método remove del árbol y pásele un subelemento para eliminar.import lxml.etree as et xml=""" <groceries> <fruit state="rotten">apple</fruit> <fruit state="fresh">pear</fruit> <punnet> <fruit state="rotten">strawberry</fruit> <fruit state="fresh">blueberry</fruit> </punnet> <fruit state="fresh">starfruit</fruit> <fruit state="rotten">mango</fruit> <fruit state="fresh">peach</fruit> </groceries> """ tree=et.fromstring(xml) for bad in tree.xpath("//fruit[@state='rotten']"): bad.getparent().remove(bad) print et.tostring(tree, pretty_print=True)
Resultado:
<groceries> <fruit state="fresh">pear</fruit> <fruit state="fresh">starfruit</fruit> <fruit state="fresh">peach</fruit> </groceries>
fuente
.remove()
requiere que el elemento sea hijo del elemento al que lo estás invocando. Por lo tanto, debe llamarlo en el elemento principal del elemento que desea eliminar. Respuesta corregida.Me encontré con una situación:
<div> <script> some code </script> text here </div>
div.remove(script)
eliminará latext here
parte que no quise.siguiendo la respuesta aquí , descubrí que
etree.strip_elements
es una solución mejor para mí, que puede controlar si eliminará o no el texto detrás conwith_tail=(bool)
param.Pero todavía no sé si esto puede usar el filtro xpath para la etiqueta. Solo pon esto para informar.
Aquí está el documento:
fuente
Como ya se mencionó, puede usar el
remove()
método para eliminar (sub) elementos del árbol:for bad in tree.xpath("//fruit[@state=\'rotten\']"): bad.getparent().remove(bad)
Pero elimina el elemento incluido el suyo
tail
, lo cual es un problema si está procesando documentos de contenido mixto como HTML:<div><fruit state="rotten">avocado</fruit> Hello!</div>
Se convierte
<div></div>
Que es lo que supongo que no siempre quieres :) He creado una función auxiliar para eliminar solo el elemento y mantener su cola:
def remove_element(el): parent = el.getparent() if el.tail.strip(): prev = el.getprevious() if prev: prev.tail = (prev.tail or '') + el.tail else: parent.text = (parent.text or '') + el.tail parent.remove(el) for bad in tree.xpath("//fruit[@state=\'rotten\']"): remove_element(bad)
De esta forma mantendrá el texto final:
<div> Hello!</div>
fuente
el.tail is not None
, ya que podría haber tal caso.También puede usar html de lxml para resolver eso:
from lxml import html xml=""" <groceries> <fruit state="rotten">apple</fruit> <fruit state="fresh">pear</fruit> <fruit state="fresh">starfruit</fruit> <fruit state="rotten">mango</fruit> <fruit state="fresh">peach</fruit> </groceries> """ tree = html.fromstring(xml) print("//BEFORE") print(html.tostring(tree, pretty_print=True).decode("utf-8")) for i in tree.xpath("//fruit[@state='rotten']"): i.drop_tree() print("//AFTER") print(html.tostring(tree, pretty_print=True).decode("utf-8"))
Debería generar esto:
//BEFORE <groceries> <fruit state="rotten">apple</fruit> <fruit state="fresh">pear</fruit> <fruit state="fresh">starfruit</fruit> <fruit state="rotten">mango</fruit> <fruit state="fresh">peach</fruit> </groceries> //AFTER <groceries> <fruit state="fresh">pear</fruit> <fruit state="fresh">starfruit</fruit> <fruit state="fresh">peach</fruit> </groceries>
fuente