Chcę użyć metody „findall”, aby zlokalizować niektóre elementy źródłowego pliku xml w module ElementTree.
Jednak źródłowy plik xml (test.xml) ma przestrzeń nazw. Obcinam część pliku xml jako przykład:
<?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>
Przykładowy kod w Pythonie znajduje się poniżej:
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>
Chociaż może to działać, ponieważ istnieje przestrzeń nazw „{http://www.test.com}”, dodawanie przestrzeni nazw przed każdym tagiem jest bardzo niewygodne.
Jak mogę zignorować przestrzeń nazw, używając metody „znajdź”, „znajdź wszystko” i tak dalej?
python
namespaces
find
elementtree
findall
KevinLeng
źródło
źródło
tree.findall("xmlns:DEAL_LEVEL/xmlns:PAID_OFF", namespaces={'xmlns': 'http://www.test.com'})
wystarczająco wygodny?tree.findall("{0}DEAL_LEVEL/{0}PAID_OFF".format('{http://www.test.com}'))
Odpowiedzi:
Zamiast modyfikować sam dokument XML, najlepiej go przeanalizować, a następnie zmodyfikować tagi w wyniku. W ten sposób możesz obsłużyć wiele przestrzeni nazw i aliasów przestrzeni nazw:
Jest to oparte na dyskusji tutaj: http://bugs.python.org/issue18304
Aktualizacja:
rpartition
zamiastpartition
zapewniać, że otrzymujesz nazwę tagu,postfix
nawet jeśli nie ma przestrzeni nazw. Tak więc możesz to skondensować:źródło
et.findall('{*}sometag')
. Jest to również zniekształcenie samego drzewa elementów, a nie tylko „wykonanie wyszukiwania ignorując przestrzenie nazw tylko tym razem, bez ponownego analizowania dokumentu itp., Zachowując informacje o przestrzeni nazw”. Cóż, w takim przypadku musisz obserwować iterację po drzewie i samemu przekonać się, czy węzeł spełnia twoje życzenia po usunięciu przestrzeni nazw.Jeśli usuniesz atrybut xmlns z xml przed jego przeanalizowaniem, nie będzie przestrzeni nazw dołączonej do każdego znacznika w drzewie.
źródło
=
znaku równości.Odpowiedzi do tej pory wyraźnie umieszczały wartość przestrzeni nazw w skrypcie. Aby uzyskać bardziej ogólne rozwiązanie, wolałbym wyodrębnić przestrzeń nazw z xml:
I użyj go w metodzie find:
źródło
namespace
Oto rozszerzenie odpowiedzi nieokąta, które również usuwa przestrzenie nazw z atrybutów:
AKTUALIZACJA: dodano,
list()
aby iterator działał (wymagany dla Pythona 3)źródło
Poprawa odpowiedzi przez ericspod:
Zamiast globalnej zmiany trybu parsowania, możemy umieścić to w obiekcie obsługującym konstrukcję with.
Można to następnie wykorzystać w następujący sposób
Piękno tego sposobu polega na tym, że nie zmienia on żadnego zachowania dla niepowiązanego kodu poza blokiem with. Skończyło się na tym, że utworzyłem to po otrzymaniu błędów w niepowiązanych bibliotekach po użyciu wersji przez ericspod, która również korzystała z expata.
źródło
xml.etree.ElementTree.XMLParser
jest w jakiś sposób zoptymalizowany, a łatanie małpyexpat
nie ma absolutnie żadnego efektu.Możesz również użyć eleganckiej konstrukcji formatowania ciągów:
lub, jeśli masz pewność, że PAID_OFF pojawia się tylko na jednym poziomie w drzewie:
źródło
Jeśli używasz
ElementTree
i niecElementTree
, możesz zmusić Expat do ignorowania przetwarzania przestrzeni nazw, zastępującParserCreate()
:ElementTree
próbuje użyć Expat, wywołując,ParserCreate()
ale nie daje opcji, aby nie podawać ciągu separatora przestrzeni nazw, powyższy kod spowoduje zignorowanie go, ale ostrzegamy, że może to zepsuć inne rzeczy.źródło
ElementTree.fromstring(s, parser=None)
próbuję przekazać do niego parser.Mogę się na to spóźnić, ale nie sądzę, żeby
re.sub
było to dobre rozwiązanie.Jednak przepisywanie
xml.parsers.expat
nie działa dla wersji Python 3.x,Głównym winowajcą jest
xml/etree/ElementTree.py
patrz dół kodu źródłowegoCo jest trochę smutne.
Rozwiązaniem jest pozbycie się go najpierw.
Przetestowano w Pythonie 3.6.
try
Instrukcja try jest przydatna w przypadku, gdy gdzieś w kodzie ponownie załadujesz lub zaimportujesz moduł dwukrotnie, otrzymujesz dziwne błędy, takie jakdo cholery, kod źródłowy etree wygląda naprawdę niechlujnie.
źródło
Połączmy odpowiedź nonagona z odpowiedzią mzjn na powiązane pytanie :
Korzystając z tej funkcji:
Utwórz iterator, aby uzyskać zarówno przestrzenie nazw, jak i przeanalizowany obiekt drzewa .
Iteruj po utworzonym iteratorze, aby uzyskać przestrzenie nazw, które możemy później przekazać
find()
lubfindall()
wywołać zgodnie z sugestią iMom0 .Myślę, że jest to najlepsze podejście dookoła, ponieważ nie ma żadnej manipulacji ani źródłowym XML ani wynikowym przeanalizowanym
xml.etree.ElementTree
wyjściem.Chciałbym również podziękować odpowiedzi Barny'ego za dostarczenie niezbędnego elementu tej układanki (że można uzyskać przeanalizowany korzeń z iteratora). Do tego czasu dwukrotnie przeszedłem drzewo XML w mojej aplikacji (raz, aby uzyskać przestrzenie nazw, drugi dla katalogu głównego).
źródło
find()
ifindall()
. Po prostu zasilasz te metody dyktatem przestrzeni nazw zparse_xml()
i używasz prefiksu przestrzeni nazw w zapytaniach. Np .:et_element.findall(".//some_ns_prefix:some_xml_tag", namespaces=xml_namespaces)