web-dev-qa-db-fra.com

comment supprimer un élément en lxml

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>"
75
ewok

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.

135
Cédric Julien

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>
28
Acorn

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
       )
11
zephor

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>
2
Messa