programing

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

iphone6s 2023. 10. 19. 22:06
반응형

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

python의 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>"

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)

만약 내가 @Acorn 버전과 비교해야 한다면, 제거할 요소가 xml의 root 노드 바로 아래에 있지 않더라도 내 것은 작동할 것입니다.

당신이 찾고 있는 것은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>

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

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

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

여기에 답을 따라서, 나는 그것을 발견했습니다.etree.strip_elements나에게 더 나은 해결책입니다. 당신은 당신이 뒤에 있는 텍스트를 삭제할지 말지를 통제할 수 있습니다.with_tail=(bool)PARAM.

하지만 아직도 이것이 태그에 xpath 필터를 사용할 수 있는지 모르겠습니다.이것은 그냥 알려주기 위해서입니다.

여기 서류가 있습니다.

strip_elements(tree_or_element, *tag_names, with_tail=True)

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

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

이렇게 해도 요소(또는 Element)는 삭제되지 않습니다.일치하더라도 전달한 트리 루트 요소).그것은 오직 후손들을 대할 뿐입니다.루트 요소를 포함하려면 이 함수를 호출하기 전에 태그 이름을 직접 확인합니다.

사용 예시:

   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
       )

이미 언급한 바와 같이, 사용할 수 있습니다.remove()트리에서 elements을(를) 삭제하는 방법:

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

그러나 그것은 그것을 포함한 요소를 제거합니다.tail, HTML과 같은 혼합 콘텐츠 문서를 처리하는 경우 문제가 됩니다.

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

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>

remove함수는 트리에서 요소를 분리하므로 XML 노드(Element, PI 또는 Comment), 그 내용(후속 항목) 및tail 여기를 보존하는 겁니다 여기서, 그를 보호하는 것은tail텍스트는 공백과 무시 가능한 공백으로 간주될 수 있는 새 줄만 포함하기 때문에 불필요합니다.

요소(및 해당 내용)를 제거하고 해당 요소를 보존하려면 다음과 같이 하십시오.tail, 다음 기능을 사용할 수 있습니다.

def remove_node(child, keep_content=False):
    """
    Remove an XML element, preserving its tail text.

    :param child: XML element to remove
    :param keep_content: ``True`` to keep child text and sub-elements.
    """
    parent = child.getparent()
    parent_text = parent.text or u""
    prev_node = child.getprevious()
    if keep_content:
        # insert: child text
        child_text = child.text or u""
        if prev_node is None:
            parent.text = u"{0}{1}".format(parent_text, child_text) or None
        else:
            prev_tail = prev_node.tail or u""
            prev_node.tail = u"{0}{1}".format(prev_tail, child_text) or None
        # insert: child elements
        index = parent.index(child)
        parent[index:index] = child[:]
    # insert: child tail
    parent_text = parent.text or u""
    prev_node = child.getprevious()
    child_tail = child.tail or u""
    if prev_node is None:
        parent.text = u"{0}{1}".format(parent_text, child_tail) or None
    else:
        prev_tail = prev_node.tail or u""
        prev_node.tail = u"{0}{1}".format(prev_tail, child_tail) or None
    # remove: child
    parent.remove(child)

데모는 다음과 같습니다.

from lxml import etree

tree = etree.XML(u"<root>text <bad>before <bad>inner</bad> after</bad> tail</root>")
bad1 = tree.xpath("//bad[1]")[0]
remove_node(bad1)

etree.dump(tree)
# <root>text  tail</root>

컨텐츠를 보존하려면 다음 작업을 수행할 수 있습니다.

tree = etree.XML(u"<root>text <bad>before <bad>inner</bad> after</bad> tail</root>")
bad1 = tree.xpath("//bad[1]")[0]
remove_node(bad1, keep_content=True)

etree.dump(tree)
# <root>text before <bad>inner</bad> after tail</root>

언급URL : https://stackoverflow.com/questions/7981840/how-to-remove-an-element-in-lxml

반응형