Розбір XML - ElementTree проти SAX і DOM


74

Python має кілька способів синтаксичного аналізу XML ...

Я розумію самі основи синтаксичного аналізу за допомогою SAX . Він функціонує як аналізатор потоку з API, керованим подіями.

Я також розумію аналізатор DOM . Він зчитує XML в пам'ять і перетворює його в об'єкти, до яких можна отримати доступ за допомогою Python.

Взагалі кажучи, було легко вибрати один із двох залежно від того, що вам потрібно було зробити, обмежень пам’яті, продуктивності тощо.

(Сподіваюся, поки що я правий.)

Починаючи з Python 2.5, у нас також є ElementTree . Як це порівняно з DOM та SAX? На кого це більше схоже? Чому це краще, ніж попередні парсери?

Відповіді:


72

ElementTree набагато простіший у використанні, оскільки він представляє дерево XML (в основному) як структуру списків, а атрибути представлені як словники.

ElementTree потребує набагато менше пам'яті для дерев XML, ніж DOM (і, отже, швидше), а накладні витрати на аналіз через iterparseпорівнянні з SAX. Крім того, iterparseповертає часткові структури, і ви можете підтримувати постійне використання пам'яті під час розбору, відкидаючи структури, як тільки їх обробляєте.

ElementTree, як і в Python 2.5, має лише невеликий набір функцій у порівнянні з повномасштабними бібліотеками XML, але цього достатньо для багатьох додатків. Якщо вам потрібен перевіряючий синтаксичний аналізатор або повна підтримка XPath, lxml - це шлях. Довгий час він був досить нестабільним, але з цим я не мав жодних проблем з 2.1.

ElementTree відхиляється від DOM, де вузли мають доступ до своїх батьків та братів і сестер. Обробка фактичних документів, а не сховищ даних, теж трохи громіздка, оскільки текстові вузли не розглядаються як фактичні вузли. У фрагменті XML

<a>This is <b>a</b> test</a>

Рядок testбуде так званим tailелементом b.

Загалом, я рекомендую ElementTree за замовчуванням для всієї обробки XML за допомогою Python, а DOM або SAX - як рішення для конкретних проблем.


2
Дякуємо, що згадали обидва наступні застереження! (Мені в моєму проекті потрібні обидва варіанти.) "Підтримка XPath ... ElementTree відхиляється від DOM, де вузли мають доступ до своїх батьків та братів і сестер."
Джон Кумбс,

1
ElementTree також має проблему, що текстовий вміст розглядається як властивість попереднього Вузла, а не як власний Вузол. Отже, у "<p> <i> коричнева </i> собака </p>" елемент <p> має 1 дочірню, а не 3. "The" - властивість для <p> та "dog" є властивістю <i> (навіть не тієї самої властивості - текст може бути відразу після закінчення <p>). Майже все, що стосується дерев, контекстів, шляхів та пошуку, працює не так, як звично, якщо ви використовуєте HTML, CSS або більшість інших питань, пов’язаних з документами.
TextGeek

13

Мінімальна реалізація DOM:

Посилання .

Python забезпечує повну реалізацію стандарту W3C XML DOM ( xml.dom ) та мінімальну, xml.dom.minidom . Цей останній є простішим і меншим, ніж повне впровадження. Однак, з точки зору розбору, він має всі плюси і мінуси стандартного DOM - тобто завантажує все в пам’ять.

Розглядаючи базовий файл XML:

<?xml version="1.0"?>
<catalog>
    <book isdn="xxx-1">
      <author>A1</author>
      <title>T1</title>
    </book>
    <book isdn="xxx-2">
      <author>A2</author>
      <title>T2</title>
    </book>
</catalog>

Можливий парсер Python з використанням minidom :

import os
from xml.dom import minidom
from xml.parsers.expat import ExpatError

#-------- Select the XML file: --------#
#Current file name and directory:
curpath = os.path.dirname( os.path.realpath(__file__) )
filename = os.path.join(curpath, "sample.xml")
#print "Filename: %s" % (filename)

#-------- Parse the XML file: --------#
try:
    #Parse the given XML file:
    xmldoc = minidom.parse(filepath)
except ExpatError as e:
    print "[XML] Error (line %d): %d" % (e.lineno, e.code)
    print "[XML] Offset: %d" % (e.offset)
    raise e
except IOError as e:
    print "[IO] I/O Error %d: %s" % (e.errno, e.strerror)
    raise e
else:
    catalog = xmldoc.documentElement
    books = catalog.getElementsByTagName("book")

    for book in books:
        print book.getAttribute('isdn')
        print book.getElementsByTagName('author')[0].firstChild.data
        print book.getElementsByTagName('title')[0].firstChild.data

Зверніть увагу, що xml.parsers.expat - це інтерфейс Python до неперевіряючого XML-аналізатора Expat (docs.python.org/2/library/pyexpat.html).

Пакет xml.dom також містить клас винятків DOMException , але він не працює в мінімальному режимі !

API ElementTree XML:

Посилання .

ElementTree набагато простіший у використанні, і він вимагає менше пам'яті, ніж XML DOM. Крім того, доступна реалізація C ( xml.etree.cElementTree ).

Можливий парсер Python за допомогою ElementTree :

import os
from xml.etree import cElementTree  # C implementation of xml.etree.ElementTree
from xml.parsers.expat import ExpatError  # XML formatting errors

#-------- Select the XML file: --------#
#Current file name and directory:
curpath = os.path.dirname( os.path.realpath(__file__) )
filename = os.path.join(curpath, "sample.xml")
#print "Filename: %s" % (filename)

#-------- Parse the XML file: --------#
try:
    #Parse the given XML file:
    tree = cElementTree.parse(filename)
except ExpatError as e:
    print "[XML] Error (line %d): %d" % (e.lineno, e.code)
    print "[XML] Offset: %d" % (e.offset)
    raise e
except IOError as e:
    print "[XML] I/O Error %d: %s" % (e.errno, e.strerror)
    raise e
else:
    catalogue = tree.getroot()

    for book in catalogue:
        print book.attrib.get("isdn")
        print book.find('author').text
        print book.find('title').text

2
Дякую! Дуже корисний. Я недостатньо впевнений, щоб редагувати його, але я думаю (а) інше не є корисним, оскільки остаточно немає: stackoverflow.com/questions/855759/python-try-else ; (b) звичайне підвищення дозволить зберегти більше, ніж підвищення e: stackoverflow.com/questions/11420464/…
Джон Кумбс,

Щодо пункту (а), так. Немає остаточного твердження просто тому, що в моєму прикладі в цьому не було потреби. Я не пам'ятаю, чому я це поставив. Однак, навіть якщо це марно в цьому випадку, наявність твердження else не є синтаксично неправильним.
Паоло Ровеллі

Щодо пункту (b), це може бути так. Однак, я думаю (у моєму прикладі) це трохи поза рамками. Дійсно, код мав бути лише простим прикладом синтаксичного аналізу XML ...
Паоло Ровеллі

1
О, я не мав на увазі, що щось із цього було «неправильним»; лише деякі пропонують редагування заради інших, які можуть прийти та скопіювати / вставити.
Джон Кумбс,

5
Зверніть увагу, що cElementTree застаріло станом на Python 3.3
gerrit

8

ElementTree має більше пітонічного API. Зараз він також є у стандартній бібліотеці, тому його використання зменшує залежності.

Я насправді віддаю перевагу lxml, оскільки він має API, такий як ElementTree, але також має приємні додаткові функції і добре працює.


7

Синтаксичний аналіз ElementTree () схожий на DOM, тоді як iterparse () - як SAX. На мій погляд, ElementTree кращий за DOM і SAX тим, що надає API, з яким легше працювати.


Крім того, я вважаю, що хочу реальну структуру, а не низку подій.
S.Lott,

1
Послідовний синтаксичний аналізатор часто досить хороший для простого синтаксичного аналізу. Я запустив Python, використовуючи sax, і перейшов на мінідом лише тоді, коли мої потреби стали занадто складними для sax. Слід додати, що я ще не використовував ElementTree, оскільки, здається, він не пропонує достатньо більше функціональних можливостей для того, щоб перенести на нього свій код.
giltay
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.