Getting text of an element based on text or attribute of other elements in XML using python scripting

Viewed 46

I want to get the name of a student for a particular detail like name, age, the complexity of gender. Suppose for the text "maths" or "married", I should get the name rick Grimes. For text "Female", I should get name Daryl. for attribute "low", I should get Maggie. I have read many docs but didn't find any function in XML python where we can traverse to any element and get the text of that based on other element text value or attribute. can someone help?

<students>
 <student>
   <name>Rick Grimes</name>
   <age>35</age>
   <subject complexity = "Medium">Maths</subject>
   <gender>Male</gender>
   <personal details>
     <status>married</status>
   </personal details>
 </student>

 <student>
   <name>Daryl Dixon </name>
   <age>33</age>
   <subject complexity = "Small">Science</subject>
   <gender>Female</gender>
   <personal details>
     <status>single</status>
   </personal details>
 </student>

 <student>
   <name>Maggie</name>
   <age>36</age>
   <subject complexity = "low">Arts</subject>
   <gender>Others</gender>
 </student>
</students>
2 Answers

You can use parsel, which is built on lxml, to group your data :

from parsel import Selector
data = """[your html above"""]
selector = Selector(data)

#create a list of first level elements in ```student``` section
keys = ['name','age','subject','gender']
coll = []

#iteration here
for entry in selector.xpath(".//student"):
    #get the dictionary pairing for element and text for each key in keys
    d = {ent : entry.xpath(f"./{ent}/text()").get() for ent in keys}

    #get data for the attribute complexity and the sub element status
    others = {'complexity' : entry.xpath(f"./subject/@complexity").get(),
              'status' : entry.xpath(".//status/text()").get()
             }

    #add to main dictionary
    d.update(others)

    #collect into a list
    coll.append(d)

print(coll)


[{'name': 'Rick Grimes',
  'age': '35',
  'subject': 'Maths',
  'gender': 'Male',
  'complexity': 'Medium',
  'status': 'married'},
 {'name': 'Daryl Dixon ',
  'age': '33',
  'subject': 'Science',
  'gender': 'Female',
  'complexity': 'Small',
  'status': 'single'},
 {'name': 'Maggie',
  'age': '36',
  'subject': 'Arts',
  'gender': 'Others',
  'complexity': 'low',
  'status': None}]

Now, you can filter based on specifics : e.g look for the name if the subject is Maths :

for entry in coll:
    if entry['subject'] == "Maths":
        print(entry['name'])

Rick Grimes

It can be done using lxml. The code below covers your first 3 tests and you can obviously add your own:

students = """[your html above"""]

import lxml.html as lh

doc = lh.fromstring(students)
married = doc.xpath('//student/name[following-sibling::personal/status[.="married"]]/text()')
math = doc.xpath('//student/name[following-sibling::subject[.="Maths"]]/text()')
females = doc.xpath('//student/name[following-sibling::gender[.="Female"]]/text()')
for mr, ma, f in zip(married,math,females):
    print(mr)
    print(ma)
    print(f)

Output:

Rick Grimes
Rick Grimes
Daryl Dixon 
Related