Die Stärke von Python und XML

(Auszug aus "Python & XML" von Christopher A. Jones & Fred L. Drake, Jr.)

Nachdem wir Sie in die XML-Welt eingeführt haben, werden wir sehen, was Python beizusteuern hat. Wir werden uns die Eigenschaften von Python anschauen, die bei XML Anwendung finden, und wir werden ein paar spezifische Beispiele von Python mit XML vorstellen. Als ausgesprochene Hochsprache enthält Python viele mächtige Datenstrukturen bereits im Kern der Sprache und in seinen Bibliotheken. Die neueren Python-Versionen, ab 2.0 und darüber, verfügen über besonders gute Unicode-Unterstützung und eine beindruckende Vielfalt an Codierungen sowie einen hervorragenden (und schnellen) XML-Parser, der Zeichendaten aus XML als Unicode-Strings zur Verfügung stellt. Pythons Standardbibliothek enthält auch Implementierungen der Industriestandardschnittstellen DOM und SAX, um mit XML-Daten zu arbeiten, und zusätzliche Unterstützung für alternative Parser und Schnittstellen ist auch verfügbar.

Natürlich könnte man das gleiche auch von anderen modernen Hochsprachen sagen. Java hat ganz gewiß eine beeindruckende Bibliothek von sehr nützlichen Datenstrukturen, und Perl bietet ebenfalls gleichwertige Datenstrukturen. Warum sollte man also Python gegenüber diesen Sprachen und ihren Bibliotheken vorziehen? Es gibt einige Eigenschaften, von denen wir kurz die wichtigsten erläutern:

  • Python-Quellcode ist einfach zu lesen und zu warten.
  • Der interaktive Interpreter macht es einfach, Codefragmente auszuprobieren.
  • Python ist unglaublich portabel, schränkt aber nicht den Zugriff auf plattformspezifische Eingenschaften ein.
  • Die objektorientierten Eigenschaften sind mächtig, ohne unverständlich zu sein.

Es gibt viele Sprachen, mit denen man das tun kann, was man auch mit Python tun kann, aber man findet selten alle »Randqualitäten« von Python in einer einzigen Sprache vereint. Diese Qualitäten machen Python nicht unbedingt mächtiger, aber einfacher anzuwenden, was die Anzahl von Programmierstunden verringert. Dadurch hat man mehr Zeit, um bessere Wege zu finden, reale Probleme zu lösen, oder man kann einfach zum nächsten Problem übergehen. Nun erläutern wir diese Eigenschaften im Detail.

Einfach zu lesen und zu warten

Als Programmiersprache zeigt Python eine bemerkenswerte Klarheit im Ausdruck. Obwohl einige Programmierer, die mit anderen Sprachen vertraut sind, erstaunt sind über die Bedeutung von Leerraum in Python, so scheint doch jeder zu denken, dass er Python-Quellcode wesentlich lesbarer macht als Sprachen, die spezielle Zeichen einführen müssen, um Strukturen im Quellcode anzugeben. Die Strukturen in Python sind nicht einfacher als jene in anderen Sprachen, aber durch die unterschiedliche Syntax sieht Quellcode in Python viel sauberer aus.

Die Verwendung von Leerraum hilft auch, kleinere stilistische Unterschiede zu vermeiden, wie das Setzen von strukturwichtigen geschweiften Klammern. Daher gibt es ein höheres Maß an visueller Konsistenz über Code von verschiedenen Programmierern hinweg. Obwohl dies vielen Programmierern als untergeordneter Punkt erscheinen mag, ist der Effekt der, daß die Wartung von Code, der von anderen Programmierern geschrieben wurde, deswegen viel einfacher wird, weil es einem leichter fällt, sich auf die tatsächliche Struktur und die Algorithmen im Code zu konzentrieren. Für den einzelnen Programmierer ist dies ein netter Nebeneffekt, aber für eine Firma bedeutet dies geringere Kosten bei der Wartung von Code.

Exploratives Programmieren in einem interaktiven Interpreter

Viele moderne, hochentwickelte Programmiersprachen stellen einen Interpreter zur Verfügung, aber wenige tun dies so erfolgreich wie Python. Andere wie z. B. Java bieten im allgemeinen überhaupt keinen Interpreter. Wenn wir Perl betrachten – eine Sprache, von der man mit Recht behauptet, daß sie von der Kommandozeile aus benutzt sehr mächtig ist –, sehen wir, dass es nicht mit einem reichhaltigen Interpreter ausgestattet ist. Wenn wir den Perl-Interpreter starten, ohne ein Skript anzugeben, wartet er einfach darauf, daß wir ein komplettes Skript auf der Konsole eingeben, und führt es aus, wenn wird damit fertig sind. Er erlaubt uns, ein paar Befehle direkt in der Kommandozeile einzugeben, aber es gibt keine Möglickeit, jeweils eine Anweisung allein auszuführen und die Ergebnisse Schritt für Schritt zu inspizieren, um zu sehen, ob jedes Codestück genau das tut, was wir erwarten. Der interaktive Python-Interpreter bietet eine reichhaltige Umgebung zum Ausführen individueller Anweisungen und zum Testen der Ergebnisse.

Portabilität ohne Einschränkungen

Der Python-Interpreter ist einer der portabelsten verfügbaren Sprach-Interpreter. Man weiß, daß er auf den verschiedensten Plattformen läuft – von PDAs und anderen eingebetteten Systemen bis hin zu einigen der mächtigsten jemals gebauten Multiprozessormaschinen. Er läuft auf mehr verschiedenen Betriebssystemen als vermutlich irgendein anderer Interpreter. Darüber hinaus kann sorgsam geschriebener Anwendungscode vieles von dieser Portabilität übernehmen. Python bietet eine Menge Abstraktionen, die gerade soviel tun, um Plattformunterschiede zu verbergen, während es dem Programmierer erlaubt ist, plattformspezifische Dienste zu benutzen, wenn nötig.

Wenn eine Anwendung Zugriff auf Einrichtungen oder Bibliotheken benötigt, die Python nicht bietet, macht Python es einfach, Erweiterungen hinzuzufügen, um solche zusätzlichen Möglichkeiten zu nutzen. Es können Zusatzmodule (normalerweise in C oder C++, aber andere Sprachen können auch verwendet werden) erstellt werden, die es Python ermöglichen, effizient auf externe Einrichtungen zuzugreifen.

Mächtige, aber zugängliche Objektorientierung

Zu einer gewissen Zeit hörte man oft, wie objektorientiertes Programmieren (OOP) die meisten technischen Probleme lösen würde, mit denen sich Programmierer in ihrem Code auseinandersetzten. Natürlich wussten es die Programmierer besser, nahmen Abstand und verwandelten die Konzepte in nützliche Werkzeuge, die man anwenden konnte, wenn es gerade paßte (obwohl das Wie und Wann sie angewendet werden sollten immer eine Diskussion wert sein dürfte). Leider sind viele Sprachen mit starker Unterstützung von OOP entweder sehr umständlich im Gebrauch (wie C++ oder in geringerem Maße Java), oder sie sind in weiten Bereichen nicht als Universalsprache akzeptiert worden (z. B. Eiffel).

Python ist anders. Die Sprache unterstützt Objektorientierung ohne einen Großteil des syntaktischen Overheads, den man in vielen weitverbreiteten objektorientierten Sprachen findet, was es sehr einfach macht, neue Objekttypen zu definieren. Im Unterschied zu vielen anderen Sprachen ist Python hochgradig polymorph; Schnittstellen werden wesentlich weniger streng definiert als in Sprachen wie C++ und Java. Das vereinfacht es, nützliche Objekte zu definieren, ohne Code zu schreiben, der nur deswegen existiert, um die Anforderungen einer Schnittstelle zu erfüllen, aber in keiner Anwendung wirklich benutzt wird. Wenn man dies mit den Vorteilen verknüpft, die aus Pythons Standardbibliothek mit einer großen Vielfalt von Schnittstellen entstehen, kann man leicht den Wert erkennen, der in wiederverwendbaren Objekten steckt. Und all dies, während die Leichtigkeit erhalten bleibt, mit der nützliche Schnittstellen implementiert werden.

Python-Werkzeuge für XML

Die Python-Werkzeuge zum Arbeiten mit XML stammen im wesentlichen aus drei Paketen. Dies sind, vom meistbenutzten zum größten:

  1. Die Python-Standardbibliothek
  2. PyXML, hergestellt von der Python XML Special Interest Group
  3. 4Suite von Fourthought, Inc.

Die Python-Standardbibliothek bietet eine minimale, aber dennoch nützliche Menge von Schnittstellen für die Arbeit mit XML, inklusive einer Schnittstelle für den beliebten XML-Parser Expat, einer Implementierung des leichtgewichtigen Simple API for XML (SAX) und einer einfachen Implementierung des Kerns vom Document Object Model (DOM). Die DOM-Implementierung unterstützt den Level 1 und einen Großteil von Level 2 der DOM-Spezifikation des W3C, implementiert jedoch die meisten optionalen Eigenschaften nicht. Das Material in der Standardbibliothek entstand ursprünglich im PyXML-Paket, und weiteres Material wurde von führenden Python-XML-Entwicklern beigesteuert.

PyXML ist ein Paket mit deutlich mehr Funktionalität. Es erweitert die Standardbibliothek um zusätzliche XML-Parser, hat eine wesentlich umfangreichere DOM-Implementierung (inklusive mehr optionaler Eigenschaften), hat Adapter, um weiteren Parsern die Unterstützung der SAX-Schnittstelle zu ermöglichen, kann XPath-Ausdrücke parsen und auswerten, kennt XSLT-Transformationen und eine Menge weiterer Hilfsmodule. Das Paket wird in Gemeinschaftsarbeit von vielen der aktivsten Python/XML-Programmierer gepflegt.

4Suite ist keine Obermenge der anderen Pakete, sondern sollte zusätzlich zu PyXML benutzt werden. Es enthält zusätzliche, auf verschiedene Anwendungen zugeschnittene DOM-Implementierungen, Unterstützung der XLink- und XPointer-Spezifikationen und Werkzeuge zum Arbeiten mit Daten des Resource Description Frameworks (RDF).

Dies sind die Pakete, die durch das gesamte Buch hindurch benutzt werden. Installation von Python und XML-Tools bietet Ihnen Informationen, wie Sie sie bekommen und installieren. Es gibt jedoch noch weitere Pakete; siehe Weitere Python-XML-Werkzeuge für kurze Beschreibungen einiger davon sowie für weitere, online verfügbare Informationen.

Die SAX- und DOM-APIs

Die beiden grundlegendsten und meistverwendeten APIs für XML-Daten sind die SAX- und DOM-Schnittstellen. Sie unterscheiden sich grundlegend voneinander, und daher ist es wichtig, unterscheiden zu können, welche davon für Ihre Anwendung die richtige ist.

SAX definiert eine relativ primitive Schnittstelle, die von XML-Parsern einfach zu unterstützen ist, aber dem Anwendungsprogrammierer weitere Detailkenntnisse hinsichtlich der Verarbeitung von Informationen in XML-Dokumenten sowie der Operationen darauf abverlangt. Es bietet jedoch den Vorteil eines geringen Overheads: Es werden keine großen Datenstrukturen aufgebaut, bevor die Anwendung selbst diese wirklich braucht. Dadurch können viele Arten der Verarbeitung wesentlich schneller als mit höherem Overhead durchgeführt werden, und es können wesentlich größere Dokumente effizient verarbeitet werden. Das liegt daran, daß es eine ereignisorientierte Schnittstelle ist. Die Verwendung von SAX ähnelt mehr der Verarbeitung von Benutzereingabe-Ereignissen einer graphischen Benutzerschnittstelle als der Bearbeitung einer vorher aufgebauten Datenstruktur. Wie erhalten Sie also »Ereignisse« von einem XML-Parser, und was könnten das für Ereignisse sein?

SAX definiert eine Anzahl von Handler-Schnittstellen, die Ihre Anwendung implementieren kann, um von Ereignissen benachrichtigt zu werden. Die Methoden dieser Objekte werden aufgerufen, wenn die entsprechenden Ereignisse im geparsten XML-Dokument angetroffen werden. Jede Methode kann als das eigentliche Ereignis betrachtet werden, was gut zu objektorientierten Parsing-Ansätzen passt. Ereignisse werden als Inhalts-, Dokumenttyp-, lexikalische und Fehlerereignisse kategorisiert. Jede Kategorie wird von einer eigenen Schnittstelle behandelt. Die Anwendung kann exakt angeben, an welchen Kategorien von Ereignissen sie interessiert ist, indem sie den Parser mit den passenden Behandlungsroutinen versorgt und all jene wegläßt, die sie nicht braucht. Pythons XML-Unterstützung bietet Basisklassen, die es Ihnen erlauben, nur die Methoden zu implementieren, an denen Sie interessiert sind, während die anderen, die Sie geerbt haben, aber nicht brauchen, einfach nichts tun.

Die meistverwendeten Ereignisse sind inhaltsbezogene Ereignisse, von denen startElement, characters und endElement die wichtigsten sind. In Die Simple API for XML betrachten wir SAX gründlicher, aber sehen wir uns vorher schnell an, wie wir mit SAX nützliche Informationen aus einem Dokument extrahieren können. Wir werden dabei ein einfaches Dokument verwenden, damit man leicht sieht, wie man daraus etwas Komplexeres aufbauen kann. So sieht das Dokument aus:

<catalog>
  <book isbn="1-56592-724-9">
    <title>The Cathedral &amp; the Bazaar</title>
    <author>Eric S. Raymond</author>
  </book>
  <book isbn="1-56592-051-1">
    <title>Making TeX Work</title>
    <author>Norman Walsh</author>
  </book>
  <!-- Stellen Sie sich hier weitere Einträge vor... -->
</catalog>

Wenn wir ein Dictionary erzeugen wollen, das die ISBN-Nummern im isbn-Attribut des book-Elements auf die Buchtitel abbildet (den Inhalt der title-Elemente), würden wir einen Content-Handler erzeugen (wie im folgenden Beispiel gezeigt), der die drei zuvor genannten Ereignisse untersucht.

Beispiel: bookhandler.py

import xml.sax.handler

class BookHandler(xml.sax.handler.ContentHandler):
    def __init__(self):    
      self.inTitle = 0
      self.mapping = {}

def startElement(self, name, attributes):
    if name == "book":
      self.buffer = ""
      self.isbn = attributes["isbn"]
      elif name == "title":
      self.inTitle = 1

def characters(self, data):
    if self.inTitle:
      self.buffer += data

def endElement(self, name):
    if name == "title":
      self.inTitle = 0
      self.mapping[self.isbn] = self.buffer

Nun die gesuchte Information zu extrahieren ist trivial. Falls sich der obige Code im Beispiel bookhandler.py befindet und unser Beispieldokument in books.xml, könnten wir dies in einer interaktiven Sitzung tun:

>>> import xml.sax
>>> import bookhandler
>>> import pprint
>>>
>>> parser = xml.sax.make_parser( )
>>> handler = bookhandler.BookHandler( )
>>> parser.setContentHandler(handler)
>>> parser.parse("books.xml")
>>> pprint.pprint(handler.mapping)
{u'1-56592-051-1': u'Making TeX Work',
 u'1-56592-724-9': u'The Cathedral & the Bazaar'}

Für Referenzmaterial zu den Methoden von Handler-Objekten siehe Die Python-SAX-API.

DOM ist so ziemlich das genaue Gegenteil von SAX. SAX bietet ein sehr kleines, bewegtes Fenster, durch das man auf das Eingabedokument blicken kann, wobei es sich auf die Anwendung verläßt, um daraus das Ganze abzuleiten. Von DOM erhält die Anwendung das gesamte Dokument, aus dem sie dann die Details selbst herausfinden muß. Anstatt der Anwendung von individuellen Ereignissen zu berichten, sobald der Parser die entsprechende Syntax im Dokument bearbeitet, erzeugt die Anwendung ein Objekt, das das gesamte Dokument als hierarchische Struktur darstellt. Obwohl es keine Anforderung gibt, daß das gesamte Dokument vollständig geparst und im Hauptspeicher vorliegt, wenn das Objekt der Anwendung übergeben wird, arbeiten die meisten Implementierungen der Einfachheit halber genau so. Einige Implementierungen vermeiden das. Aber es ist durchaus möglich, eine DOM-Implementierung zu schreiben, die das Dokument verzögert parst oder eine Art persistenten Speicher benutzt, um das geparste Dokument dort statt im Hauptspeicher aufzubewahren.

Das DOM bietet Objekte, die Knoten genannt werden, die einer Anwendung gegenüber Teile eines Dokuments darstellen. Es gibt verschiedene Knotentypen, von denen jeder für ein anderes Konstrukt benutzt wird. Es ist wichtig zu verstehen, daß die DOM-Knoten nicht direkt den Ereignissen in SAX entsprechen, obwohl viele ähnlich sind. Am leichtesten sieht man den Unterschied, indem man sich anschaut, wie Elemente und deren Inhalte mit beiden APIs dargestellt werden. In SAX wird ein Element mit Start- und Endereignissen dargestellt, und sein Inhalt wird von allen Ereignissen dargestellt, die dazwischen liegen. Das DOM bietet ein einzelnes Objekt, das das Element darstellt, und Methoden, mit denen die Anwendung an Kindknoten gelangen kann, die den Inhalt des Elements darstellen. Es existieren verschiedene Knotentypen für Elemente, Text und so ziemlich alles andere, was in XML-Dokumenten vorkommt.

Wir werden in Das Document Object Model mehr ins Detail gehen und einige erweiterte Beispiele zu DOM sehen, eine detaillierte Referenz zur DOM-API finden Sie in Python-DOM-API. Um einen ersten Eindruck von DOM zu bekommen, schreiben wir schnell etwas Code, der das gleiche tut, was wir im Beispiel bookhandler.py mit SAX gemacht haben, aber diesmal verwenden wir die einfache DOM-Implementierung aus der Python-Standardbibliothek, wie im folgenden Beispiel gezeigt.

Beispiel: dombook.py

import pprint

import xml.dom.minidom
from xml.dom.minidom import Node

doc = xml.dom.minidom.parse("books.xml")

mapping = {}

for node in doc.getElementsByTagName("book"):
   isbn = node.getAttribute("isbn")
   L = node.getElementsByTagName("title")
   for node2 in L:
      title = ""
      for node3 in node2.childNodes:
        if node3.nodeType == Node.TEXT_NODE:
          title += node3.data
       mapping[isbn] = title

# Abbildung hat nun den gleichen Wert wie im SAX-Beispiel:
pprint.pprint(mapping)

Es sollte klar sein, daß wir es hier mit etwas völlig anderem zu tun haben! Obwohl es im DOM-Beispiel ungefähr gleich viel Code gibt, kann es sehr schwer sein, wiederverwendbare Komponenten zu entwickeln, während etwas Erfahrung mit SAX oft die Richtung für wiederverwendbare Komponenten mit nur wenig Refactoring anzeigt. Es ist möglich, DOM-Code wiederzuverwenden, aber die dazu nötige Einstellung ist eine ganz andere. Zum Ausgleich bietet DOM die Möglichkeit, ein Dokument an beliebigen Stellen bearbeiten zu können, wenn man es vollständig kennt, und der Dokumentinhalt kann auf verschiedene Weise von verschiedenen Teilen der Anwendung extrahiert werden, ohne das Dokument mehr als einmal parsen zu müssen. Bei einigen Anwendungen ist dies ein ausreichender Grund, DOM statt SAX zu gebrauchen.

Weitere Möglichkeiten, um Informationen zu extrahieren

Mit SAX und DOM haben wir mächtige Werkzeuge, um mit XML zu arbeiten, aber sie verlangen eine Menge Code und Aufmerksamkeit im Detail, will man sie effektiv in großen Anwendungen einsetzen. In beiden Fällen braucht es für die Arbeit mit komplexen Daten eine Menge Aufwand, nur um jene interessanten Teile von XML-Dokumenten zu extrahieren, die die Daten enthalten. Aber welche Art von Werkzeugen würden wir normalerweise benutzen, wenn wir es mit komplexen Datenmengen zu tun haben? Zwei, die einem einfallen, sind höhere Abstraktionen (wie z. B. APIs, die mehr Arbeit verrichten, und spezialisierte, aufgabenorientierte Sprachen) sowie Techniken zur Vorverarbeitung (Datentransformation von einer Form in eine andere, die für die gegebene Aufgabe besser geeignet ist). Glücklicherweise stehen uns beide zur Verfügung, wenn wir in Python mit XML arbeiten.

Wenn ein XML-Benutzer einen Teil eines Dokuments mit einer Menge von möglicherweise komplexen Kriterien spezifizieren möchte, benutzt er eine Sprache, die es ihm ermöglicht, die Spezifikation kurz und präzise zu schreiben. Diese Sprache heißt XML Path Language oder kurz XPath. Unterstützung für XPath existiert im 4Suite-Paket und wurde vor kurzem auch dem PyXML-Paket hinzugefügt. Mit XPath kann eine Abfrage formuliert werden, die Knoten aus einem DOM-Baum selektiert, basierend auf den Elementnamen, Attributwerten, dem Textinhalt und den Beziehungen zwischen den Knoten. In Abfragen von XML mit XPath behandeln wir einige Details von XPath inklusive der Frage, wie man es mit einem DOM-Baum in Python benutzt.

Möglicherweise möchten Sie aber auch gerne ein neues Dokument, das entweder weniger Informationen enthält oder diese auf eine andere Weise anordnet. Dafür brauchen wir eine Möglichkeit, eine Transformation eines Dokuments anzugeben, die ein anderes Dokument erzeugt. Das leisten die XML Stylesheet Language Transformations (XSLT). Ursprünglich als Teil einer neuen Spezifikation für Stylesheets entwickelt, ist XSLT eine XML-basierte Sprache, mit der Transformationen von XML in andere Formate definiert werden. XSLT wird meistens mit XML oder HTML als Ausgabeformat benutzt. Transformation von XML mit XSLT beschreibt diese Sprache und zeigt, wie man sie in Python benutzt.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Python & XML bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2002 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Python & XML" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de