J'ai besoin de supprimer complètement les éléments, basés sur le contenu d'un attribut, en utilisant le lxml de python. Exemple:
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)
J'aimerais imprimer ceci:
<groceries>
<fruit state="fresh">pear</fruit>
<fruit state="fresh">starfruit</fruit>
<fruit state="fresh">Peach</fruit>
</groceries>
Existe-t-il un moyen de le faire sans stocker une variable temporaire et y imprimer manuellement, comme:
newxml="<groceries>\n"
for elt in tree.xpath('//fruit[@state=\'fresh\']'):
newxml+=et.tostring(elt)
newxml+="</groceries>"
Utilisez la méthode remove
d'un xmlElement:
tree=et.fromstring(xml)
for bad in tree.xpath("//fruit[@state=\'rotten\']"):
bad.getparent().remove(bad) # here I grab the parent of the element to call the remove directly on it
print et.tostring(tree, pretty_print=True, xml_declaration=True)
Si je devais comparer avec la version @Acorn, la mienne fonctionnera même si les éléments à supprimer ne sont pas directement sous le nœud racine de votre xml.
Vous recherchez la fonction remove
. Appelez la méthode remove de l'arborescence et passez-lui un sous-élément à supprimer.
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)
Résultat:
<groceries>
<fruit state="fresh">pear</fruit>
<fruit state="fresh">starfruit</fruit>
<fruit state="fresh">Peach</fruit>
</groceries>
J'ai rencontré une situation:
<div>
<script>
some code
</script>
text here
</div>
div.remove(script)
supprimera la partie text here
que je ne voulais pas.
après la réponse ici , j'ai trouvé que etree.strip_elements
est une meilleure solution pour moi, que vous pouvez contrôler si vous supprimerez ou non le texte derrière avec with_tail=(bool)
param .
Mais je ne sais toujours pas si cela peut utiliser le filtre xpath pour la balise. Il suffit de mettre cela pour informer.
Voici le doc:
strip_elements (tree_or_element, * tag_names, with_tail = True)
Supprimez tous les éléments avec les noms de balise fournis dans une arborescence ou une sous-arborescence. Cela supprimera les éléments et leur sous-arbre entier, y compris tous leurs attributs, le contenu du texte et les descendants. Il supprimera également le texte de queue de l'élément, sauf si vous définissez explicitement l'option d'argument de mot clé
with_tail
Sur False.Les noms de balises peuvent contenir des caractères génériques comme dans
_Element.iter
.Notez que cela ne supprimera pas l'élément (ou l'élément racine ElementTree) que vous avez passé même s'il correspond. Il ne traitera que ses descendants. Si vous souhaitez inclure l'élément racine, vérifiez son nom de balise directement avant même d'appeler cette fonction.
Exemple d'utilisation ::
strip_elements(some_element, 'simpletagname', # non-namespaced tag '{http://some/ns}tagname', # namespaced tag '{http://some/other/ns}*' # any tag from a namespace lxml.etree.Comment # comments )
Comme déjà mentionné, vous pouvez utiliser la méthode remove()
pour supprimer les (sous) éléments de l'arborescence:
for bad in tree.xpath("//fruit[@state=\'rotten\']"):
bad.getparent().remove(bad)
Mais il supprime l'élément, y compris son tail
, ce qui est un problème si vous traitez des documents à contenu mixte comme HTML:
<div><fruit state="rotten">avocado</fruit> Hello!</div>
Devient
<div></div>
Ce qui est, je suppose, ce que vous ne voulez pas toujours :) J'ai créé une fonction d'assistance pour supprimer uniquement l'élément et garder sa queue:
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 cette façon, il conservera le texte de queue:
<div> Hello!</div>