Parsowanie XML z przestrzenią nazw w Pythonie za pomocą 'ElementTree'
Mam następujący XML, który chcę przeanalizować używając Pythona ElementTree
:
<rdf:RDF xml:base="http://dbpedia.org/ontology/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns="http://dbpedia.org/ontology/">
<owl:Class rdf:about="http://dbpedia.org/ontology/BasketballLeague">
<rdfs:label xml:lang="en">basketball league</rdfs:label>
<rdfs:comment xml:lang="en">
a group of sports teams that compete against each other
in Basketball
</rdfs:comment>
</owl:Class>
</rdf:RDF>
Chcę znaleźć wszystkie znaczniki owl:Class
, a następnie wyodrębnić wartość wszystkich instancji rdfs:label
wewnątrz nich. Używam następującego kodu:
tree = ET.parse("filename")
root = tree.getroot()
root.findall('owl:Class')
Z powodu przestrzeni nazw, dostaję następujący błąd.
SyntaxError: prefix 'owl' not found in prefix map
Próbowałem przeczytać dokument na http://effbot.org/zone/element-namespaces.htm ale nadal nie jestem w stanie tego uruchomić, ponieważ powyższy XML ma wiele zagnieżdżone przestrzenie nazw.
Uprzejmie daj mi znać, jak zmienić kod, aby znaleźć wszystkie tagi owl:Class
.
6 answers
ElementTree nie jest zbyt inteligentny w zakresie przestrzeni nazw. Musisz dać .find()
, findall()
and iterfind()
methods an explicit namespace dictionary. Nie jest to dobrze udokumentowane:
namespaces = {'owl': 'http://www.w3.org/2002/07/owl#'} # add more as needed
root.findall('owl:Class', namespaces)
Prefiksy są tylko wyszukane w parametrze namespaces
, który podajesz. Oznacza to, że możesz użyć dowolnego prefiksu przestrzeni nazw; API dzieli część owl:
, wyszukuje odpowiedni adres URL przestrzeni nazw w słowniku namespaces
, a następnie zmienia wyszukiwanie, aby szukać wyrażenia XPath {http://www.w3.org/2002/07/owl}Class
. Możesz oczywiście użyć tej samej składni:
root.findall('{http://www.w3.org/2002/07/owl#}Class')
Jeśli możesz przełączyć się na lxml
biblioteka rzeczy są lepsze; ta biblioteka obsługuje ten sam ElementTree API, ale zbiera przestrzenie nazw dla Ciebie w atrybucie .nsmap
na elementach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-30 15:20:49
Oto Jak to zrobić z lxml bez konieczności kodowania przestrzeni nazw lub skanowania ich tekstu (jak wspomina Martijn Pieters):
from lxml import etree
tree = etree.parse("filename")
root = tree.getroot()
root.findall('owl:Class', root.nsmap)
UPDATE :
5 lat później wciąż mam do czynienia z wariacjami tego problemu. lxml pomaga jak pokazałem powyżej, ale nie w każdym przypadku. Komentatorzy mogą mieć ważny punkt dotyczący tej techniki, jeśli chodzi o łączenie dokumentów, ale myślę, że większość ludzi ma trudności z prostym wyszukiwaniem dokumentów.
Oto kolejna sprawa i jak sobie z nią poradziłem:
<?xml version="1.0" ?><Tag1 xmlns="http://www.mynamespace.com/prefix">
<Tag2>content</Tag2></Tag1>
Xmlns bez prefiksu oznacza, że znaczniki bez prefiksu otrzymują domyślną przestrzeń nazw. Oznacza to, że szukając Tag2, musisz dołączyć przestrzeń nazw, aby ją znaleźć. Jednak lxml tworzy wpis nsmap z None jako kluczem i nie mogłem znaleźć sposobu, aby go wyszukać. Tak więc, stworzyłem nowy słownik przestrzeni nazw jak ten
namespaces = {}
# response uses a default namespace, and tags don't mention it
# create a new ns map using an identifier of our choice
for k,v in root.nsmap.iteritems():
if not k:
namespaces['myprefix'] = v
e = root.find('myprefix:Tag2', namespaces)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-30 18:47:24
Uwaga: jest to odpowiedź przydatna dla standardowej biblioteki ElementTree Pythona bez używania zakodowanych na twardo przestrzeni nazw.
Aby wyodrębnić prefiksy przestrzeni nazw i URI z danych XML można użyć funkcji ElementTree.iterparse
, analizującej tylko zdarzenia start przestrzeni nazw (start-ns):
>>> from io import StringIO
>>> from xml.etree import ElementTree
>>> my_schema = u'''<rdf:RDF xml:base="http://dbpedia.org/ontology/"
... xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
... xmlns:owl="http://www.w3.org/2002/07/owl#"
... xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
... xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
... xmlns="http://dbpedia.org/ontology/">
...
... <owl:Class rdf:about="http://dbpedia.org/ontology/BasketballLeague">
... <rdfs:label xml:lang="en">basketball league</rdfs:label>
... <rdfs:comment xml:lang="en">
... a group of sports teams that compete against each other
... in Basketball
... </rdfs:comment>
... </owl:Class>
...
... </rdf:RDF>'''
>>> my_namespaces = dict([
... node for _, node in ElementTree.iterparse(
... StringIO(my_schema), events=['start-ns']
... )
... ])
>>> from pprint import pprint
>>> pprint(my_namespaces)
{'': 'http://dbpedia.org/ontology/',
'owl': 'http://www.w3.org/2002/07/owl#',
'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
'rdfs': 'http://www.w3.org/2000/01/rdf-schema#',
'xsd': 'http://www.w3.org/2001/XMLSchema#'}
Wtedy słownik może być przekazany jako argument do funkcji wyszukiwania:
root.findall('owl:Class', my_namespaces)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-21 08:15:53
Aby uzyskać Przestrzeń nazw w jej formacie, np. {myNameSpace}
, możesz wykonać następujące czynności:
root = tree.getroot()
ns = re.match(r'{.*}', root.tag).group(0)
W ten sposób możesz użyć go później w kodzie, aby znaleźć węzły, np. za pomocą interpolacji łańcuchów (Python 3).
link = root.find(f"{ns}link")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-11 08:33:15
Używałem podobnego kodu do tego i stwierdziłem, że zawsze warto przeczytać dokumentację... jak zwykle!
Findall() znajdzie tylko elementy, które są bezpośrednimi potomkami bieżącego znacznika . Więc nie wszystkie.
Może warto spróbować, aby Twój kod działał z następującymi, zwłaszcza jeśli masz do czynienia z dużymi i złożonymi plikami xml, dzięki czemu pod-podelementy (itp.). Jeśli wiesz, gdzie znajdują się elementy w Twoim xml, więc będzie dobrze! Pomyślałem, że warto o tym pamiętać.
root.iter()
Ref: https://docs.python.org/3/library/xml.etree.elementtree.html#finding-interesting-elements "Element.findall () znajduje tylko elementy ze znacznikiem, które są bezpośrednimi potomkami bieżącego elementu. Element.find() znajduje pierwsze dziecko z określonym znacznikiem i elementem.tekst uzyskuje dostęp do zawartości tekstowej elementu. Element.get () uzyskuje dostęp do atrybutów elementu:"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-16 09:51:36
Moje rozwiązanie opiera się na komentarzu @ Martijn Pieters:
register_namespace
wpływa tylko na serializację, a nie wyszukiwanie.
Sztuczka polega na używaniu różnych słowników do serializacji i wyszukiwania.
namespaces = {
'': 'http://www.example.com/default-schema',
'spec': 'http://www.example.com/specialized-schema',
}
Teraz zarejestruj wszystkie przestrzenie nazw do parsowania i pisania:
for name, value in namespaces.iteritems():
ET.register_namespace(name, value)
Do wyszukiwania (find()
, findall()
, iterfind()
) potrzebujemy niepustego prefiksu. Przekazać te funkcje zmodyfikowanym słownikiem (tutaj modyfikuję oryginalny słownik, ale to musi być wykonane dopiero po zarejestrowaniu przestrzeni nazw).
self.namespaces['default'] = self.namespaces['']
Teraz funkcje z rodziny find()
mogą być używane z prefiksem default
:
print root.find('default:myelem', namespaces)
Ale
tree.write(destination)
Nie używa prefiksów dla elementów w domyślnej przestrzeni nazw.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-04 00:49:07