Get attribute of complex element using lxml

£可爱£侵袭症+ 提交于 2019-12-25 02:42:31

问题


I have a simple file XML like below:

    <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>

I want to parse it using lxml and get the value of it: With brandName, it just need:

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

If I want to get into abbrev attribute of it.

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

With maxspeed, i can get the value of maxspeed by:

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

or:

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

Now, I want to get the attribute of unit inside , I have tried a lot of different way but I'm failed. The error most of time is:

    'NoneType' object has no attribute 'attrib'

Here are several ways I tried and it failed:

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

Could you please give me some hint why it doesn't work? Thank you very much!

UPDATE 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>

回答1:


The .find method on an lxml Element will only search the direct sub-children of that element. so for example in this 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>

You can use the root Elements .find method to locate the brandname element, or the maxspeed element, but the search will not traverse inside these inner elements.

So you could for example do something like this:

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

From this returned element you can access the attributes.

If you'd like to search through all the elements within an XML doc, you can use the .iter() method. So for the previous example you could say:

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

EDIT: Here is a small fully functional example using the xml you provided:

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

Hope it helps.




回答2:


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)

yields

mph


来源:https://stackoverflow.com/questions/8161530/get-attribute-of-complex-element-using-lxml

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!