Вопрос-Ответ

Parsing XML with namespace in Python via 'ElementTree'

Синтаксический анализ XML с пространством имен в Python с помощью 'ElementTree'

У меня есть следующий XML, который я хочу проанализировать с помощью Python 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>

Я хочу найти все owl:Class теги, а затем извлечь значение всех rdfs:label экземпляров внутри них. Я использую следующий код:

tree = ET.parse("filename")
root = tree.getroot()
root.findall('owl:Class')

Из-за пространства имен я получаю следующую ошибку.

SyntaxError: prefix 'owl' not found in prefix map

Я попытался прочитать документ по адресу http://effbot.org/zone/element-namespaces.htm но я все еще не могу заставить это работать, поскольку приведенный выше XML имеет несколько вложенных пространств имен.

Пожалуйста, дайте мне знать, как изменить код, чтобы найти все owl:Class теги.

Переведено автоматически
Ответ 1

Вам нужно предоставить методам .find(), findall() и iterfind() явный словарь пространства имен:

namespaces = {'owl': 'http://www.w3.org/2002/07/owl#'} # add more as needed

root.findall('owl:Class', namespaces)

Префиксы только просматриваются в namespaces передаваемом вами параметре. Это означает, что вы можете использовать любой префикс пространства имен, который вам нравится; API отделяет owl: часть, ищет соответствующий URL пространства имен в namespaces словаре, затем изменяет поиск для поиска выражения XPath {http://www.w3.org/2002/07/owl}Class вместо этого. Вы, конечно, и сами можете использовать тот же синтаксис:

root.findall('{http://www.w3.org/2002/07/owl#}Class')

Также смотрите Синтаксический анализ XML с использованием пространств имен раздел документации ElementTree .

Если вы можете переключиться на lxml библиотеку, то дела пойдут лучше; эта библиотека поддерживает тот же ElementTree API, но собирает для вас пространства имен в .nsmap атрибуте элементов и, как правило, имеет превосходную поддержку пространств имен.

Ответ 2

Вот как это сделать с помощью lxml без необходимости жестко кодировать пространства имен или сканировать текст для них (как упоминает Мартинс Питерс):

from lxml import etree
tree = etree.parse("filename")
root = tree.getroot()
root.findall('owl:Class', root.nsmap)

Обновить:

5 лет спустя я все еще сталкиваюсь с вариациями этой проблемы. lxml помогает, как я показал выше, но не в каждом случае. У комментаторов может быть обоснованная точка зрения относительно этого метода, когда дело доходит до объединения документов, но я думаю, что большинство людей испытывают трудности с простым поиском документов.

Вот еще один случай и как я с ним справился:

<?xml version="1.0" ?><Tag1 xmlns="http://www.mynamespace.com/prefix">
<Tag2>content</Tag2></Tag1>

xmlns без префикса означает, что теги без префикса получают это пространство имен по умолчанию. Это означает, что при поиске Tag2 вам нужно включить пространство имен, чтобы найти его. Однако lxml создает запись nsmap с ключом None в качестве ключа, и я не смог найти способ ее поиска. Итак, я создал новый словарь пространства имен, подобный этому

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)
Ответ 3

Примечание: Этот ответ полезен для стандартной библиотеки ElementTree в Python без использования жестко закодированных пространств имен.

Для извлечения префиксов пространства имен и URI из XML-данных вы можете использовать ElementTree.iterparse функцию, анализирующую только события запуска пространства имен (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#'}

Затем словарь может быть передан в качестве аргумента функциям поиска:

root.findall('owl:Class', my_namespaces)
Ответ 4

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

findall() найдет только элементы, которые являются прямыми дочерними элементами текущего тега. Итак, не совсем ВСЕ.

Возможно, вам стоит попробовать заставить свой код работать со следующим, особенно если вы имеете дело с большими и сложными XML-файлами, так что эти подэлементы (и т.д.) Также включены. Если вы сами знаете, где находятся элементы в вашем xml, то, я полагаю, все будет в порядке! Просто подумал, что это стоит запомнить.

root.iter()

ссылка: https://docs.python.org/3/library/xml.etree.elementtree.html#finding-interesting-elements
"Element.findall() находит только элементы с тегом, которые являются прямыми дочерними элементами текущего элемента. Element.find() находит первого дочернего элемента с определенным тегом, а Element.text обращается к текстовому содержимому элемента. Element.get() обращается к атрибутам элемента: "

python