Модуль Python ElementTree: как игнорировать пространство имен файлов XML для поиска подходящего элемента при использовании методов «find», «findall»

Я хочу использовать метод "найти все" найти некоторые элементы исходного XML-файла в модуле ElementTree.

Однако исходный XML-файл (test.xml) имеет пространство имен. Я усекаю часть XML-файла в качестве образца:



    Updates
    9/26/2012 10:30:34 AM
    All Rights Reserved.
    newlicense.htm
    
        N
        

Пример кода Python ниже:

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 

Хотя это может работать, потому что есть пространство имен{} Http://www.test.com», Это'очень неудобно добавлять пространство имен перед каждым тегом.

Как я могу игнорировать пространство имен при использовании метода "находить", "найти все" и так далее?

 KevinLeng16 нояб. 2012 г., 10:17
Спасибо большое. Я пробую ваш метод, и он может работать. Это'удобнее, чем у меня, но этовсе еще немного неловко. Знаете ли вы, что в модуле ElementTree нет другого правильного метода для решения этой проблемы или такого метода нет вообще?
 iMom016 нояб. 2012 г., 09:57
Являетсяtree.findall("xmlns:DEAL_LEVEL/xmlns:PAID_OFF", namespaces={'xmlns': 'http://www.test.com'}) достаточно удобно?

Ответы на вопрос(7)

тогдане может быть пространством имен, добавленным к каждому тегу в дереве.

import re

xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)
 Parthian Shot16 июн. 2015 г., 22:11
 Michael Rice15 сент. 2014 г., 22:53
Просто к сведению, это работает только на Python 2.x Python 3.x будет бросать: TypeError: can 'использовать строковый шаблон на байтовоподобном объекте
 Mike15 февр. 2015 г., 20:48
-1 манипулирование xml с помощью регулярного выражения перед синтаксическим анализом просто неправильно. хотя в некоторых случаях это может сработать, это не должно быть самым популярным ответом и не должно использоваться в профессиональном приложении.
 david.barkhuizen20 мая 2014 г., 21:00
+100, кто-то монетный двор этого разработчика криптовалюты
 nonagon18 сент. 2014 г., 21:38
Это работало во многих случаях для меня, но затем я столкнулся с несколькими пространствами имен и псевдонимами пространства имен. Смотрите мой ответ для другого подхода, который обрабатывает эти случаи.

Если ты'повторное использованиеElementTree и неcElementTree Вы можете заставить Expat игнорировать обработку пространства имен, заменив:ParserCreate()

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

ElementTree пытается использовать Expat по телефонуParserCreate() но не предоставляет никакой опции, чтобы не предоставлять строку разделителя пространства имен, приведенный выше код заставит ее игнорировать, но следует предупредить, что это может сломать другие вещи.

 est20 мар. 2019 г., 13:24
@ Барни это 'все еще возможно,ElementTree.fromstring(s, parser=None) Я пытаюсь передать парсер к нему.
 ericspod19 февр. 2019 г., 15:31
cElemTree устарела, но естьслежка за типами выполняется с помощью ускорителей Си, Код C нет вызов в экспат, так что да, это решение не работает.
 barny13 февр. 2019 г., 15:52
В Python 3.7.2 (и, возможно, раньше) УДАЛИТЬ это 'больше невозможно избежать использования cElementTree, поэтому этот обходной путь может быть невозможен :-(
 lijat11 дек. 2018 г., 13:42
Это лучший способ, чем другие текущие ответы, так как он не зависит от обработки строк

является продолжением нонагонаs ответ, который также удаляет пространства имен из атрибутов:

from StringIO import StringIO
import xml.etree.ElementTree as ET

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
    for at in 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:

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

И используйте его в методе find:

namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text
 Kashyap18 мар. 2014 г., 20:29
Слишком много, чтобы предполагать, что есть только одинnamespace

Вместо глобального изменения режима разбора мы можем обернуть это в объект, поддерживающий конструкцию with.

from xml.parsers import expat

class DisableXmlNamespaces:
    def __enter__(self):
            self.oldcreate = expat.ParserCreate
            expat.ParserCreate = lambda encoding, sep: self.oldcreate(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")

Прелесть этого способа в том, что он не меняет никакого поведения для несвязанного кода вне блока with. Я создал это после получения ошибок в несвязанных библиотеках после использования версии ericspod, которая также использовала expat.

 AndreasT26 дек. 2018 г., 01:42
Это сладко и полезно! Спас мой день! +1

Лучше всего разобрать его, а затем изменить теги в результате. Таким образом, вы можете обрабатывать несколько пространств имен и псевдонимов пространства имен:

from StringIO import StringIO
import xml.etree.ElementTree as ET

# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
    if '}' in el.tag:
        el.tag = el.tag.split('}', 1)[1]  # strip all namespaces
root = it.root

Это основано на обсуждении здесь:http://bugs.python.org/issue18304

 Jess11 окт. 2014 г., 05:08
Это. Это это это Многочисленные пространства имен должны были стать для меня смертью.
 TraceKira29 авг. 2016 г., 21:28
Это работает путем удаления строки, но когда я сохраняю XML-файл, используя write (...), пространство имен исчезает из начала XML xmlns = "бла» . пропадает Пожалуйста посоветуй
 Tomasz Gandor14 нояб. 2014 г., 16:12
Хорошо, это хорошо и более продвинутый, но все же этонеet.findall('{*}sometag'), И это также искажает само дерево элементов, а не простовыполнять поиск, игнорируя пространства имен только в этот раз, без повторного анализа документа и т. д., сохраняя информацию о пространстве имен ", Ну, для этого случая вам, очевидно, нужно перебрать дерево и убедиться, соответствует ли узел вашим пожеланиям после удаления пространства имен.

оки:

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

или, если выуверен, чтоPAID_OFF появляется только на одном уровне в дереве:

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

Ваш ответ на вопрос