programing

파이썬 요소트리 모듈:"find", "findall" 메서드를 사용할 때 일치하는 요소를 찾기 위해 XML 파일의 네임스페이스를 무시하는 방법

topblog 2023. 6. 12. 21:03
반응형

파이썬 요소트리 모듈:"find", "findall" 메서드를 사용할 때 일치하는 요소를 찾기 위해 XML 파일의 네임스페이스를 무시하는 방법

▁▁method다싶▁the니습▁i 방법을 사용하고 .findall xml 합니다.ElementTree모듈.

그러나 원본 xml 파일(test.xml)에는 네임스페이스가 있습니다.xml 파일의 일부를 샘플로 잘라냅니다.

<?xml version="1.0" encoding="iso-8859-1"?>
<XML_HEADER xmlns="http://www.test.com">
    <TYPE>Updates</TYPE>
    <DATE>9/26/2012 10:30:34 AM</DATE>
    <COPYRIGHT_NOTICE>All Rights Reserved.</COPYRIGHT_NOTICE>
    <LICENSE>newlicense.htm</LICENSE>
    <DEAL_LEVEL>
        <PAID_OFF>N</PAID_OFF>
        </DEAL_LEVEL>
</XML_HEADER>

샘플 파이썬 코드는 다음과 같습니다.

from xml.etree import ElementTree as ET
tree = ET.parse(r"test.xml")
el1 = tree.findall("DEAL_LEVEL/PAID_OFF") # Return None
el2 = tree.findall("{http://www.test.com}DEAL_LEVEL/{http://www.test.com}PAID_OFF") # Return <Element '{http://www.test.com}DEAL_LEVEL/PAID_OFF' at 0xb78b90>

사용하지만"{http://www.test.com}"각 태그 앞에 네임스페이스를 추가하는 것은 매우 불편합니다.

다음과 같은 함수를 사용할 때 네임스페이스를 무시하려면 어떻게 해야 합니까?find,findall, ...?

XML 문서 자체를 수정하는 대신 구문 분석한 다음 결과의 태그를 수정하는 것이 가장 좋습니다.이렇게 하면 여러 네임스페이스 및 네임스페이스 별칭을 처리할 수 있습니다.

from io import StringIO  # for Python 2 import from StringIO instead
import xml.etree.ElementTree as ET

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    _, _, el.tag = el.tag.rpartition('}') # strip ns
root = it.root

이것은 여기서 논의한 내용을 바탕으로 합니다.

구문 분석하기 전에 xml에서 xmlns 특성을 제거하면 트리의 각 태그 앞에 네임스페이스가 추가되지 않습니다.

import re

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)

지금까지의 답변은 스크립트에 네임스페이스 값을 명시적으로 입력했습니다.보다 일반적인 솔루션의 경우 xml에서 네임스페이스를 추출하는 것이 좋습니다.

import re
def get_namespace(element):
  m = re.match('\{.*\}', element.tag)
  return m.group(0) if m else ''

찾기 방법으로 사용합니다.

namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text

다음은 속성에서 네임스페이스를 제거하기 위한 @nonagon 응답(태그에서 네임스페이스를 제거함)의 확장입니다.

import io
import xml.etree.ElementTree as ET

# instead of ET.fromstring(xml)
it = ET.iterparse(io.StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
    for at in list(el.attrib.keys()): # strip namespaces of attributes too
        if '}' in at:
            newat = at.split('}', 1)[1]
            el.attrib[newat] = el.attrib[at]
            del el.attrib[at]
root = it.root

분명히 이것은 XML을 영구적으로 훼손하는 것이지만 고유하지 않은 태그 이름이 없고 원래 이름 공간이 필요한 파일을 쓰지 않기 때문에 허용된다면 액세스가 훨씬 쉬워질 수 있습니다.

ericspod를 통한 답변 개선:

구문 분석 모드를 전체적으로 변경하는 대신 구문을 지원하는 개체로 이 모드를 래핑할 수 있습니다.

from xml.parsers import expat

class DisableXmlNamespaces:
    def __enter__(self):
        self.old_parser_create = expat.ParserCreate
        expat.ParserCreate = lambda encoding, sep: self.old_parser_create(encoding, None)

    def __exit__(self, type, value, traceback):
        expat.ParserCreate = self.oldcreate

다음과 같이 사용할 수 있습니다.

import xml.etree.ElementTree as ET
with DisableXmlNamespaces():
     tree = ET.parse("test.xml")

이 방법의 장점은 블록 외부의 관련 없는 코드에 대한 동작을 변경하지 않는다는 것입니다.우연히 expat를 사용한 ericspod 버전을 사용한 후 관련이 없는 라이브러리에서 오류가 발생하여 이를 생성하게 되었습니다.

우아한 문자열 형식 지정 구조도 사용할 수 있습니다.

ns='http://www.test.com'
el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))

또는 PAYED_OFF가 트리의 한 수준에만 표시되는 것이 확실한 경우:

el2 = tree.findall(".//{%s}PAID_OFF" % ns)

3를 python 3.5의 할 수 .find()를 들어예를들면,

ns= {'xml_test':'http://www.test.com'}
tree = ET.parse(r"test.xml")
el1 = tree.findall("xml_test:DEAL_LEVEL/xml_test:PAID_OFF",ns)

설명서 링크: - https://docs.python.org/3.5/library/xml.etree.elementtree.html#parsing-xml-with-namespaces

나는 이것에 늦을지도 모르지만 나는 생각하지 않습니다.re.sub좋은 해결책입니다.

그러나 다시 쓰기는xml.parsers.expatPython 3.x 버전에서는 작동하지 않습니다.

주범은.xml/etree/ElementTree.py소스 코드 하단 참조

# Import the C accelerators
try:
    # Element is going to be shadowed by the C implementation. We need to keep
    # the Python version of it accessible for some "creative" by external code
    # (see tests)
    _Element_Py = Element

    # Element, SubElement, ParseError, TreeBuilder, XMLParser
    from _elementtree import *
except ImportError:
    pass

그건 좀 슬픈 일입니다.

해결책은 그것을 먼저 없애는 것입니다.

import _elementtree
try:
    del _elementtree.XMLParser
except AttributeError:
    # in case deleted twice
    pass
else:
    from xml.parsers import expat  # NOQA: F811
    oldcreate = expat.ParserCreate
    expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

Python 3.6에서 테스트되었습니다.

해라try명령문은 코드의 어딘가에서 모듈을 다시 로드하거나 두 번 가져오면 다음과 같은 이상한 오류가 발생할 때 유용합니다.

  • 최대 재귀 깊이가 초과됨
  • 특성 오류: XMLParser

젠장, 트리 소스 코드가 정말 지저분해 보입니다.

사용 중인 경우ElementTree그리고 아닌cElementTreeExpat이 네임스페이스 처리를 무시하도록 강제할 수 있습니다.ParserCreate():

from xml.parsers import expat
oldcreate = expat.ParserCreate
expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)

ElementTree호출하여 Expat 사용을 시도합니다.ParserCreate()그러나 네임스페이스 구분자 문자열을 제공하지 않는 옵션을 제공하지 않습니다. 위의 코드는 이 문자열을 무시하게 하지만 이로 인해 다른 항목이 손상될 수 있음을 경고합니다.

관련 질문에 대한 노나곤 답변과 mzjn의 답변을 결합해 보겠습니다.

def parse_xml(xml_path: Path) -> Tuple[ET.Element, Dict[str, str]]:
    xml_iter = ET.iterparse(xml_path, events=["start-ns"])
    xml_namespaces = dict(prefix_namespace_pair for _, prefix_namespace_pair in xml_iter)
    return xml_iter.root, xml_namespaces

이 기능을 사용하여 다음 작업을 수행합니다.

  1. 네임스페이스와 구문 분석된 트리 개체를 모두 가져오는 반복기를 만듭니다.

  2. 작성된 반복기를 반복하여 나중에 전달할 수 있는 네임스페이스를 가져옵니다.find()또는findall()아이맘0이 제안한 통화.

  3. 구문 분석된 트리의 루트 요소 개체 및 네임스페이스를 반환합니다.

소스 XML이나 결과적으로 구문 분석되는 조작이 없기 때문에 이것이 가장 좋은 접근 방식이라고 생각합니다.xml.etree.ElementTree관련된 모든 것을 출력합니다.

저는 또한 balmy의 대답이 이 퍼즐의 필수적인 부분을 제공한 것이라고 믿고 싶습니다(반복기에서 구문 분석된 뿌리를 얻을 수 있습니다).그 때까지 저는 실제로 애플리케이션에서 XML 트리를 두 번 통과했습니다(한 번은 네임스페이스, 두 번째는 루트).

우연히 XSD 조건부 유형 할당 기본 유형 혼동이라는 답이 나왔습니다.이것은 주제 질문에 대한 정확한 대답은 아니지만 네임스페이스가 중요하지 않은 경우에 적용될 수 있습니다.

<?xml version="1.0" encoding="UTF-8"?>
<persons xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="test.xsd">
    <person version="1">
        <firstname>toto</firstname>
        <lastname>tutu</lastname>
    </person>
</persons>

참고 항목: https://www.w3.org/TR/xmlschema-1/ #xsi_schemaLocation

나한테 효과가 있어요.응용프로그램에서 XML 유효성 검사 절차를 호출합니다.또한 XML을 편집할 때 PyCharm의 유효성 강조 표시 및 자동 완성을 빨리 확인하고 싶습니다.이것.noNamespaceSchemaLocation속성은 내가 필요로 하는 것을 해줍니다.

다시 확인됨

from xml.etree import ElementTree as ET
tree = ET.parse("test.xml")
el1 = tree.findall("person/firstname")
print(el1[0].text)
el2 = tree.find("person/lastname")
print(el2.text)

반품자

>python test.py
toto
tutu

언급URL : https://stackoverflow.com/questions/13412496/python-elementtree-module-how-to-ignore-the-namespace-of-xml-files-to-locate-ma

반응형