Получить атрибут сложного элемента, используя lxml - PullRequest
0 голосов
/ 17 ноября 2011

У меня есть простой файл XML, как показано ниже:

    <brandName type="http://example.com/codes/bmw#" abbrev="BMW" value="BMW" />BMW</brandName>
      <maxspeed>
        <value>250</value>
        <unit type="http://example.com/codes/units#" value="miles per hour" abbrev="mph" />
      </maxspeed>

Я хочу проанализировать его с помощью lxml и получить его значение: С брендом просто нужно:

    'brand_name'  : m.findtext(NS+'brandName')

Если я хочу получить его сокращенный атрибут.

    'brand_name'  : m.findtext(NS+'brandName').attrib['abbrev']

С maxspeed я могу получить значение maxspeed:

    'maxspeed_value'                  : m.findtext(NS+'maxspeed/value'),

или

    'maxspeed_value'                  : m.find(NS+'maxspeed/value').text,

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

    'NoneType' object has no attribute 'attrib'

Вот несколько способов, которые я попробовал, и это не удалось:

    'maxspeed_unit'                  : m.find(NS+'maxspeed/value').attrib['abbrev'],
    'maxspeed_unit'                  : (m.find(NS+'maxspeed/value'))get('abbrev'),

Не могли бы вы дать мне подсказку, почему это не работает? Большое спасибо!

ОБНОВЛЕНИЕ XML:

    <Car xmlns="http://example.com/vocab/xml/cars#">
     <dateStarted>2011-02-05</dateStarted>
     <dateSold>2011-02-13</dateSold>
    <name type="http://example.com/codes/bmw#" abbrev="X6" value="BMW X6" >BMW X6</name>
    <brandName type="http://example.com/codes/bmw#" abbrev="BMW" value="BMW" />BMW</brandName>
      <maxspeed>
        <value>250</value>
        <unit type="http://example.com/codes/units#" value="miles per hour" abbrev="mph" />
      </maxspeed>
      <route type="http://example.com/codes/routes#" abbrev="HW" value="Highway" >Highway</route>
      <power>
        <value>180</value>
        <unit type="http://example.com/codes/units#" value="powerhorse" abbrev="ph" />
      </power>
      <frequency type="http://example.com/codes/frequency#" value="daily" >Daily</frequency>  
    </Car>

Ответы [ 2 ]

0 голосов
/ 17 ноября 2011

Метод .find в элементе lxml будет искать только прямые дочерние элементы этого элемента.так, например, в этом xml:

<root>
    <brandName type="http://example.com/codes/bmw#" abbrev="BMW" value="BMW">BMW</brandName>
    <maxspeed>
        <value>250</value>
        <unit type="http://example.com/codes/units#" value="miles per hour" abbrev="mph" />
    </maxspeed>
</root>

Вы можете использовать корневой метод Elements .find, чтобы найти элемент фирменного наименования или элемент maxspeed, но поиск не будет проходить внутри этих внутренних элементов.

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

root.find('maxspeed').find('unit') #returns the unit Element

Из этого возвращенного элемента вы можете получить доступ к атрибутам.

Если вы хотите выполнить поиск по всем элементам внутриXML-документ, вы можете использовать метод .iter ().Таким образом, для предыдущего примера вы могли бы сказать:

for element in root.iter(tag='unit'):
    print element #This would print all the unit elements in the document.

РЕДАКТИРОВАТЬ: Вот небольшой полнофункциональный пример с использованием предоставленного вами XML:

import lxml.etree
from StringIO import StringIO

def ns_join(element, tag, namespace=None):
    '''Joins the namespace and tag together, and
    returns the fully qualified name.
    @param element - The lxml.etree._Element you're searching
    @param tag - The tag you're joining
    @param namespace - (optional) The Namespace shortname default is None'''

    return '{%s}%s' % (element.nsmap[namespace], tag)

def parse_car(element):
    '''Parse a car element, This will return a dictionary containing
    brand_name, maxspeed_value, and maxspeed_unit'''

    maxspeed = element.find(ns_join(element,'maxspeed'))
    return { 
        'brand_name' : element.findtext(ns_join(element,'brandName')), 
        'maxspeed_value' : maxspeed.findtext(ns_join(maxspeed,'value')), 
        'maxspeed_unit' : maxspeed.find(ns_join(maxspeed, 'unit')).attrib['abbrev']
        }

#Create the StringIO object to feed to the parser.
XML = StringIO('''
<Reports>
    <Car xmlns="http://example.com/vocab/xml/cars#">
        <dateStarted>2011-02-05</dateStarted>
        <dateSold>2011-02-13</dateSold>
        <name type="http://example.com/codes/bmw#" abbrev="X6" value="BMW X6" >BMW X6</name>
        <brandName type="http://example.com/codes/bmw#" abbrev="BMW" value="BMW" >BMW</brandName>
        <maxspeed>
            <value>250</value>
            <unit type="http://example.com/codes/units#" value="miles per hour" abbrev="mph" />
        </maxspeed>
        <route type="http://example.com/codes/routes#" abbrev="HW" value="Highway" >Highway</route>
        <power>
            <value>180</value>
            <unit type="http://example.com/codes/units#" value="powerhorse" abbrev="ph" />
        </power>
        <frequency type="http://example.com/codes/frequency#" value="daily" >Daily</frequency>  
    </Car>
</Reports>
''')

#Get the root element object of the xml
car_root_element = lxml.etree.parse(XML).getroot()

# For each 'Car' tag in the root element,
# we want to parse it and save the list as cars
cars = [ parse_car(element) 
    for element in car_root_element.iter() if element.tag.endswith('Car')]

print cars

Надеюсь, этопомогает.

0 голосов
/ 17 ноября 2011
import lxml.etree as ET
content='''
<Car xmlns="http://example.com/vocab/xml/cars#">
 <brandName type="http://example.com/codes/bmw#" abbrev="BMW" value="BMW" >BMW</brandName>
   <maxspeed>
     <value>250</value>
     <unit type="http://example.com/codes/units#" value="miles per hour" abbrev="mph" />
   </maxspeed>
 </Car>
'''

doc=ET.fromstring(content)
NS = 'http://example.com/vocab/xml/cars#'
# print(ET.tostring(doc,pretty_print=True))
for x in doc.xpath('//ns:maxspeed/ns:unit/@abbrev',namespaces={'ns': NS}):
    print(x)

выходы

mph
...