lxml'de bir öğe nasıl kaldırılır


84

Python'un lxml'sini kullanarak bir özniteliğin içeriğine bağlı olarak öğeleri tamamen kaldırmam gerekiyor. Misal:

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)

Bunu yazdırmak istiyorum:

<groceries>
  <fruit state="fresh">pear</fruit>
  <fruit state="fresh">starfruit</fruit>
  <fruit state="fresh">peach</fruit>
</groceries>

Bunu geçici bir değişkeni kaydetmeden ve manuel olarak yazdırmadan yapmanın bir yolu var mı?

newxml="<groceries>\n"
for elt in tree.xpath('//fruit[@state=\'fresh\']'):
  newxml+=et.tostring(elt)

newxml+="</groceries>"

Yanıtlar:


155

removeBir xmlElement yöntemini kullanın :

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)

@Acorn sürümüyle karşılaştırmam gerekirse, kaldırılacak öğeler doğrudan xml'nizin kök düğümünün altında olmasa bile benimki çalışacaktır.


1
Bu cevap ile Acorn tarafından verilen cevap arasındaki farklar hakkında yorum yapabilir misiniz?
ewok

Element sınıfının 'pop' yöntemine sahip olmaması çok yazık.
pumazi

29

removeİşlevi arıyorsunuz . Ağacın kaldırma yöntemini çağırın ve onu kaldırmak için bir alt öğeye geçirin.

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)

Sonuç:

<groceries>
  <fruit state="fresh">pear</fruit>
  <fruit state="fresh">starfruit</fruit>
  <fruit state="fresh">peach</fruit>
</groceries>

Benim için lxml ile ilgili tüm cevapları aldınız, değil mi? ;-)
ewok

Bu cevap ile Cedric'in verdiği cevap arasındaki farklar hakkında yorum yapabilir misiniz?
ewok

3
Ah, .remove()öğenin, çağırdığınız öğenin çocuğu olmasını gerektiren gerçeği gözden kaçırdım . Bu yüzden, kaldırmak istediğiniz öğenin üstünden çağırmanız gerekir. Cevap düzeltildi.
Acorn

@Acorn: İşte bu, kaldırılacak öğe doğrudan kök düğümün altında olmasaydı, başarısız olurdu.
Cédric Julien

17
@ewok: Cédric'e benden 1 saniye önce cevap verdiği için kabul et ve daha da önemlisi cevabı doğruydu :)
Acorn

14

Bir durumla karşılaştım:

<div>
    <script>
        some code
    </script>
    text here
</div>

div.remove(script)text hereistemediğim kısmı kaldıracak .

Buradaki cevabı takiben, bunun etree.strip_elementsbenim için daha iyi bir çözüm olduğunu buldum, arkadaki metni with_tail=(bool)param ile silip kaldırmayacağınızı kontrol edebilirsiniz .

Ama yine de bunun etiket için xpath filtresini kullanıp kullanamayacağını bilmiyorum. Bunu sadece bilgilendirmek için koyun.

İşte belge:

strip_elements (tree_or_element, * tag_names, with_tail = True)

Bir ağaçtan veya alt ağaçtan sağlanan etiket adlarına sahip tüm öğeleri silin. Bu, tüm öznitelikleri, metin içeriği ve alt öğeleri dahil olmak üzere öğeleri ve alt ağacının tamamını kaldıracaktır. with_tailAnahtar kelime argümanı seçeneğini açıkça False olarak ayarlamadığınız sürece, öğenin kuyruk metnini de kaldıracaktır .

Etiket adları gibi joker karakterler içerebilir _Element.iter.

Bunun, eşleşse bile geçtiğiniz öğeyi (veya ElementTree kök öğesini) silmeyeceğini unutmayın. Sadece torunlarını tedavi edecek. Kök öğeyi dahil etmek istiyorsanız, bu işlevi çağırmadan önce doğrudan etiket adını kontrol edin.

Örnek kullanım ::

   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
       )

2

Daha önce de belirtildiği gibi, remove()ağaçtan (alt) öğeleri silmek için yöntemi kullanabilirsiniz :

for bad in tree.xpath("//fruit[@state=\'rotten\']"):
  bad.getparent().remove(bad)

Ancak tail, HTML gibi karma içerikli belgeleri işliyorsanız bir sorun olan kendi öğesi de dahil olmak üzere öğeyi kaldırır :

<div><fruit state="rotten">avocado</fruit> Hello!</div>

Olur

<div></div>

Sanırım her zaman istemediğiniz bir şey var :) Sadece öğeyi kaldırmak ve kuyruğunu korumak için yardımcı fonksiyon oluşturdum:

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)

Bu şekilde kuyruk metnini koruyacaktır:

<div> Hello!</div>

1
el.tail is not NoneBöyle bir durum olabileceğinden, kontrol edin .
Eivydas Vilčinskas

1

Bunu çözmek için lxml'deki html'yi de kullanabilirsiniz:

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"))

Bunu çıktı almalıdır:

//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>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.