lxml에서 요소를 제거하는 방법


84

파이썬의 lxml을 사용하여 속성의 내용을 기반으로 요소를 완전히 제거해야합니다. 예:

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)

다음을 인쇄하고 싶습니다.

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

다음과 같이 임시 변수를 저장하고 수동으로 인쇄하지 않고이를 수행 할 수있는 방법이 있습니까?

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

newxml+="</groceries>"

답변:


153

removexmlElement 의 메소드를 사용하십시오 .

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 버전과 비교해야한다면 제거 할 요소가 xml의 루트 노드 바로 아래에 있지 않아도 작동합니다.


1
이 답변과 Acorn에서 제공 한 답변의 차이점에 대해 말씀해 주시겠습니까?
ewok

Element 클래스에 'pop'메서드가없는 것은 유감입니다.
pumazi

29

당신은 remove기능을 찾고 있습니다. 트리의 remove 메서드를 호출하고 제거 할 하위 요소를 전달합니다.

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)

결과:

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

저에게 lxml 관련 답변을 모두 가지고 계시죠? ;-)
ewok 2011

이 답변과 Cedric이 제공 한 답변의 차이점에 대해 말씀해 주시겠습니까?
ewok

3
아, 나는 .remove()요소가 당신이 부르는 요소의 자식이어야 한다는 사실을 간과 했습니다. 따라서 제거하려는 요소의 부모에서 호출해야합니다. 답변이 수정되었습니다.
Acorn 2011

@Acorn : 제거 할 요소가 루트 노드 바로 아래에 있지 않으면 실패했을 것입니다.
Cédric Julien

17
@ewok : Cédric 이 나보다 1 초 일찍 대답 했으므로 수락을 주시고, 더 중요한 것은 그의 대답이 정확하다는 것입니다. :)
Acorn

13

한 가지 상황을 만났습니다.

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

div.remove(script)의도 text here하지 않은 부분을 제거합니다 .

여기에 대한 답변에 따라 param으로 etree.strip_elements텍스트를 제거할지 여부를 제어 할 수있는 더 나은 솔루션 이라는 것을 알았습니다 with_tail=(bool).

하지만 여전히 이것이 태그에 xpath 필터를 사용할 수 있는지 모르겠습니다. 알리기 위해 이것을 넣으십시오.

다음은 문서입니다.

strip_elements (tree_or_element, * tag_names, with_tail = True)

제공된 태그 이름을 가진 모든 요소를 ​​트리 또는 하위 트리에서 삭제합니다. 이렇게하면 모든 속성, 텍스트 콘텐츠 및 하위 항목을 포함하여 요소와 전체 하위 트리가 제거됩니다. with_tail키워드 인수 옵션을 명시 적 으로 False로 설정하지 않으면 요소의 꼬리 텍스트도 제거됩니다 .

태그 이름은에서와 같이 와일드 카드를 포함 할 수 있습니다 _Element.iter.

일치하더라도 전달한 요소 (또는 ElementTree 루트 요소)는 삭제되지 않습니다. 그 후손 만 취급합니다. 루트 요소를 포함하려면이 함수를 호출하기 직전에 태그 이름을 확인하십시오.

사용 예 ::

   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

이미 언급했듯이이 remove()메서드를 사용 하여 트리에서 (하위) 요소를 삭제할 수 있습니다 .

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

그러나 tailHTML과 같은 혼합 콘텐츠 문서를 처리하는 경우 문제가 되는를 포함하는 요소를 제거합니다 .

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

된다

<div></div>

나는 당신이 항상 원하지 않는 것을 가정합니다 :) 요소 만 제거하고 꼬리를 유지하는 도우미 함수를 만들었습니다.

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)

이렇게하면 꼬리 텍스트가 유지됩니다.

<div> Hello!</div>

1
el.tail is not None그러한 경우가있을 수 있으므로을 확인하십시오 .
Eivydas Vilčinskas 19.01.17

1

lxml의 html을 사용하여 해결할 수도 있습니다.

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

다음과 같이 출력되어야합니다.

//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>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.