2015-06-26 9 views
1

Я разбираю XML-документ, который мы будем получать от поставщика каждый день, и он сильно использует пространства имен. Я минимизировал проблему до крошечного подмножества здесь:Как получить атрибут элемента с пространством имен

Есть некоторые элементы, которые мне нужны для синтаксического анализа, и все они являются дочерними элементами другого элемента, который имеет в нем определенный атрибут.
Я могу использовать lxml.etree.Element.findall(TAG, root.nsmap), чтобы найти узлы-кандидаты, чей атрибут мне нужно проверить.

Затем я пытаюсь проверить атрибут каждого из этих Элементов с помощью имени, которое, как я знаю, он использует: здесь конкретно ss:Name. Если значение этого атрибута является тем, что я хочу, я собираюсь погрузиться глубже в упомянутый Элемент, чтобы продолжать делать другие вещи.

Как я могу это сделать?

XML-я разборе примерно

<FOO xmlns="SOME_REALLY_LONG_STRING" 
some gorp declaring a bunch of namespaces one of which is 
xmlns:ss="THE_VERY_SAME_REALLY_LONG_STRING_AS_ROOT" 
> 
    <child_of_foo> 
     .... 
    </child_of_foo> 
    ... 
    <SomethingIWant ss:Name="bar" OTHER_ATTRIB_I_DONT_WANT> 
     .... 
     <MoreThingsToLookAtLater> 
      .... 
     </MoreThingsToLookAtLater> 
     .... 
    </SomethingIWant> 
    ... 
</FOO> 

Я нашел первый элемент я хотел SomethingIWant как так (в конечном счете, я хочу их все, поэтому я найти все)

import lxml 
from lxml import etree 

tree = etree.parse(myfilename) 
root = tree.getroot() 
# i want just the first one for now 
my_sheet = root.findall('ss:RecordSet', root.nsmap)[0] 

Теперь я хочу получить атрибут ss:Name этого элемента, чтобы проверить его, но я не уверен, как это сделать.

Я знаю, что my_sheet.attrib отобразит мне необработанный URI, за которым следует имя атрибута, но я этого не хочу. Мне нужно проверить, имеет ли он определенное значение для атрибута specificc namespaced. (Потому что, если это неправильно, я могу полностью пропустить этот элемент из дальнейшей обработки).

Я пробовал использовать lxml.etree.ElementTree.attrib.get(), но, похоже, я ничего не получаю.

Любые идеи?

+0

ли вы на самом деле с помощью 'lxml' библиотеки или только XML-парсер от стандартной питон? Что такое 'etree' точно,' lxml.etree'? – har07

+0

обновлено снова на основе @ har07 answer – UpAndAdam

+1

Итак, вы хотите получить атрибут в пространстве имен из ранее выбранного элемента, например 'my_sheet' в фрагменте кода. В этом случае мое обновление имеет отношение к вашему обновленному вопросу. – har07

ответ

1

Одним из преимуществ lxml над стандартным питон XML парсера lxml «s полная поддержка XPath 1.0 Specfication через xpath() меня ThOD. Поэтому я бы использовал метод xpath() большую часть времени.Работа примера для текущего случая:

from lxml import etree 

xml = """<FOO xmlns="SOME_REALLY_LONG_STRING" 
xmlns:ss="THE_VERY_SAME_REALLY_LONG_STRING_AS_ROOT" 
> 
    <child_of_foo> 
     .... 
    </child_of_foo> 
    ... 
    <SomethingIWant ss:Name="bar"> 
     .... 
    </SomethingIWant> 
    ... 
</FOO>""" 

root = etree.fromstring(xml) 
ns = {'ss': 'THE_VERY_SAME_REALLY_LONG_STRING_AS_ROOT'} 

# i want just the first one for now 
result = root.xpath('//@ss:Name', namespaces=ns)[0] 
print(result) 

выход:

bar 

UPDATE:

Модифицированного пример, демонстрирующий, как получить атрибут в пространстве имен из текущего element:

ns = {'ss': 'THE_VERY_SAME_REALLY_LONG_STRING_AS_ROOT', 'd': 'SOME_REALLY_LONG_STRING'} 

element = root.xpath('//d:SomethingIWant', namespaces=ns)[0] 
print(etree.tostring(element)) 

attribute = element.xpath('@ss:Name', namespaces=ns)[0] 
print(attribute) 

выход:

<SomethingIWant xmlns="SOME_REALLY_LONG_STRING" xmlns:ss="THE_VERY_SAME_REALLY_LONG_STRING_AS_ROOT" ss:Name="bar"> 
     .... 
    </SomethingIWant> 
    ... 

bar 
+0

Спасибо, очень полезно! Это выглядит намного правильнее, но есть тонкость, которую я недостаточно прояснил, что я обновляю вопрос. Если этот «результат» - это то, что я ищу, мне понадобится ссылка на этот элемент в целом потому что это означает, что у меня есть дети, которых я должен обрабатывать. Обновление вопроса, чтобы подчеркнуть это; моя вина. – UpAndAdam

+0

Обновлен мой ответ, чтобы показать, как использовать 'xpath()', чтобы получить как атрибут, так и его родительский элемент. Надеюсь, я правильно понимаю ваше требование. – har07

+0

Единственная морщина заключается в том, что URI для 'd' буквально используется для' ss', и поэтому они никогда не используют 'd'; Но я думаю, что могу фактически использовать 'ss' для этого ... в любом случае это только упрощает то, что вы мне дали; который идеально! Еще раз спасибо – UpAndAdam

0

Я уверен, что это ужасно НЕПИТОНИЧЕСКИЙ, но не идеальный способ сделать это; и кажется, что там должно быть лучше ... но я обнаружил, что я мог бы сделать это:

SS_REAL = "{%s}" % root.nsmap.get('ss')

и тогда я мог бы сделать: my_sheet.get(SS_REAL + "NAME")

Это заставляет меня, что я хочу .. но это не может быть право способ сделать это ..

-1

Мое решение:

https://pastebin.com/F5HAw6zQ

#!/usr/bin/python 
# -*- coding: utf-8 -*- 

from sys import argv 
import xml.etree.ElementTree as ET 

NS = 'x' # default namespace key # (any string is OK) 

class XMLParser(object): 
    def __init__(self): 
     self.ns = {}  # namespace dict 
     self.root = None # XML's root element 

    # extracts the namespace (usually from the root element) 
    def get_namespace(self, tag): 
     return tag.split('}')[0][1:] 

    # loads the XML file (here: from string) 
    def load_xml(self, xmlstring): 
     root = ET.fromstring(xmlstring) 
     self.root = root 
     self.ns[NS] = self.get_namespace(root.tag) 
     return True 

    # transforms XPath without namespaces to XPath with namespace 
    # AND detects if last element is an attribute 
    def ns_xpath(self, xpath): 
     tags = xpath.split('/') 
     if tags[-1].startswith('@'): 
      attrib = tags.pop()[1:] 
     else: 
      attrib = None 
     nsxpath = '/'.join(['%s:%s' % (NS, tag) for tag in tags]) 
     return nsxpath, attrib 

    # `find` and `findall` method in one place honoring attributes in XPath 
    def xfind(self, xpath, e=None, findall=False): 
     if not e: 
      e = self.root 
     if not findall: 
      f = e.find 
     else: 
      f = e.findall 
     nsxpath, attrib = self.ns_xpath(xpath) 
     e = f(nsxpath, self.ns) 
     if attrib: 
      return e.get(attrib) 
     return e 

def main(xmlstring): 
    p = XMLParser() 
    p.load_xml(xmlstring) 
    xpaths = { 
     'Element a:': 'a', 
     'Element b:': 'a/b', 
     'Attribute c:': 'a/b/@c' 
     } 
    for key, xpath in xpaths.items(): 
     print key, xpath, p.xfind(xpath) 

if __name__ == "__main__": 
    xmlstring = """<root xmlns="http://www.example.com"> 
     <a> 
      <b c="Hello, world!"> 
      </b> 
     </a> 
    </root>""" 
    main(xmlstring) 

Результат:

Element a: a <Element '{http://www.example.com}a' at 0x2bbcb30> 
Element b: a/b <Element '{http://www.example.com}b' at 0x2bbcb70> 
Attribute c: a/b/@c Hello, world! 
Смежные вопросы