2015-07-24 4 views
3

Я пытаюсь разобрать этот XML-строку, используя ElementTree в Python,Python XML разбора с ElementTree возвращает None

данные хранятся в виде строки,

xml = '''<?xml version="1.0" encoding="utf-8"?> 
<SearchResults xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
<Student> 
    <RollNumber>1</RollNumber> 
    <Name>Abel</Name> 
    <PhoneNumber>Not Included</PhoneNumber> 
    <Email>[email protected]</Email> 
    <Grade>7</Grade> 
</Student> 
<Student> 
    <RollNumber>2</RollNumber> 
    <Name>Joseph</Name> 
    <PhoneNumber>Not Included</PhoneNumber> 
    <Email>[email protected]</Email> 
    <Grade>7</Grade> 
</Student> 
<Student> 
    <RollNumber>3</RollNumber> 
    <Name>Mike</Name> 
    <PhoneNumber>Not Included</PhoneNumber> 
    <Email>[email protected]</Email> 
    <Grade>7</Grade> 
</Student> 
</SearchResults>''' 

Код я использовал для разбора этой строки как XML,

from xml.etree import ElementTree 

xml = ElementTree.fromstring(xml) 

results = xml.findall('Student') 

for students in results: 
    for student in students: 
     print student.get('Name') 

print results выводит результаты в виде элементов,

[<Element 'Student' at 0x7feb615b4ad0>, <Element 'Student' at 0x7feb615b4c50>, <Element 'Student' at 0x7feb615b4e10>] 

внутри для цикла, print students печатает то же самое,

<Element 'Student' at 0x7fd722d88ad0> 
<Element 'Student' at 0x7fd722d88c50> 
<Element 'Student' at 0x7fd722d88e10> 

Во всяком случае, когда я пытаюсь получить имя студента с помощью print student.get('Name'), программа возвращает None.

Что я пытаюсь сделать, это вывести значения из xml для каждого тега и построить dict.

ответ

3

У вас есть двойной цикл здесь:

for students in results: 
    for student in students: 
     print student.get('Name') 

students является один элемент <Student>. Повторяя это, вы получаете отдельные элементы , содержащиеся в этот элемент. Эти элементы (<RollNumber>, <Name> и т. Д.) Не имеют атрибута Name.

Метод .get() имеет только атрибуты доступа, но вы, кажется, хотите получить элемент <Name>. Используйте .find() или выражение XPath здесь вместо:

for student in results: 
    name = student.find('Name') 
    if name is not None: 
     print name.text 

или

for student_name in xml.findall('.//Student/Name'): 
    print name.text 
2

Если вы новичок в обработке XML:

  • lxml является быстрая и мощная библиотека для взаимодействия с XML в питон. В стандартной библиотеке нет полной поддержки xpath.
  • xpath - язык запросов для изучения документов XML, он имеет крутую кривую обучения, но с помощью StackOverflow легко получить справку. xpath настолько полезен, что я начал использовать JSON для XML при использовании API только для того, чтобы писать запросы xpath вместо безумного вложенного разыменования словарей.

from lxml import etree 
from pprint import pprint 

doc = etree.XML('''<?xml version="1.0" encoding="utf-8"?> 
<SearchResults xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
<Student> 
    <RollNumber>1</RollNumber> 
    <Name>Abel</Name> 
    <PhoneNumber>Not Included</PhoneNumber> 
    <Email>[email protected]</Email> 
    <Grade>7</Grade> 
</Student> 
<Student> 
    <RollNumber>2</RollNumber> 
    <Name>Joseph</Name> 
    <PhoneNumber>Not Included</PhoneNumber> 
    <Email>[email protected]</Email> 
    <Grade>7</Grade> 
</Student> 
<Student> 
    <RollNumber>3</RollNumber> 
    <Name>Mike</Name> 
    <PhoneNumber>Not Included</PhoneNumber> 
    <Email>[email protected]</Email> 
    <Grade>7</Grade> 
</Student> 
</SearchResults>''') 

def first(seq,default=None): 
    for item in seq: 
    return item 
    return default 

def simple_children_to_dict(element): 
    result = {} 
    for child in element: 
    result[child.tag] = child.text 
    return result 

def get_by_rollnumber(number,search_results): 
    student_element = first(search_results.xpath('Student[./RollNumber=$number]',number=number)) 
    if student_element is None: 
    raise Exception("Student Number {0} not found".format(number)) 
    return simple_children_to_dict(student_element) 

def get_all_students(search_results): 
    students = [] 
    for student_element in doc.xpath('Student'): 
    students.append(simple_children_to_dict(student_element)) 
    return students 

Тогда:

>>> pprint(get_by_rollnumber(2,doc)) 
{'Email': '[email protected]', 
'Grade': '7', 
'Name': 'Joseph', 
'PhoneNumber': 'Not Included', 
'RollNumber': '2'} 
>>> 
>>> pprint(get_all_students(doc)) 
[{'Email': '[email protected]', 
    'Grade': '7', 
    'Name': 'Abel', 
    'PhoneNumber': 'Not Included', 
    'RollNumber': '1'}, 
{'Email': '[email protected]', 
    'Grade': '7', 
    'Name': 'Joseph', 
    'PhoneNumber': 'Not Included', 
    'RollNumber': '2'}, 
{'Email': '[email protected]', 
    'Grade': '7', 
    'Name': 'Mike', 
    'PhoneNumber': 'Not Included', 
    'RollNumber': '3'}] 

Тонкости:

  • xpath запросы обычно возвращает результирующий набор, потому что большинство запросов может иметь более чем один матч. Следовательно, используется вспомогательная функция first.
Смежные вопросы