Zanurkuj w Pythonie/Parsowanie XML-a
Parsowanie XML-a
edytujJak już mówiliśmy, parsowanie XML-a właściwie jest bardzo proste: jedna linijka kodu. Co z tym zrobimy dalej, to już zależy wyłącznie od nas samych.
Przykład. Ładowanie dokumentu XML (tym razem naprawdę)
>>> from xml.dom import minidom #(1) >>> xmldoc = minidom.parse('~/zanurkuj_w_pythonie/py/kgp/binary.xml') #(2) >>> xmldoc #(3) <xml.dom.minidom.Document instance at 010BE87C> >>> print xmldoc.toxml() #(4) <?xml version="1.0" ?> <grammar> <ref id="bit"> <p>0</p> <p>1</p> </ref> <ref id="byte"> <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\ <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p> </ref> </grammar>
- Jak już widzieliśmy w poprzednim podrozdziale, ta instrukcja importuje moduł
minidom
z pakietuxml.dom
. - Tutaj jest ta jedna linia kodu, która wykonuje całą robotę:
minidom.parse
pobiera jeden argument i zwraca sparsowaną reprezentację dokumentu XML. Argumentem może być wiele rzeczy; w tym wypadku jest to po prostu nazwa pliku dokumentu XML na lokalnym dysku. (Aby kontynuować musimy zmienić ścieżkę tak, aby wskazywała na katalog, w którym przechowujemy pobrane z sieci przykłady.) Możemy także jako parametr przekazać obiekt pliku lub nawet obiekt plikopodobny (ang. file-like object). Skorzystamy z tej elastyczności później w tym rozdziale. - Obiektem zwróconym przez
minidom.parse
jest obiektDocument
, który jest klasą pochodną klasyNode
. Ten obiektDocument
jest korzeniem złożonej struktury drzewiastej połączonych ze sobą obiektów Pythona, która w pełni reprezentuje dokument XML przekazany funkcjiminidom.parse
. toxml
jest metodą klasyNode
(a zatem jest też dostępna w obiekcieDocument
otrzymanym zminidom.parse
).toxml
wypisuje XML reprezentowany przez dany obiektNode
. Dla węzła, którym jest obiektDocument
, wypisuje ona cały dokument XML.
Skoro już mamy dokument XML w pamięci, możemy zacząć po nim wędrować.
Przykład. Pobieranie węzłów potomnych
>>> xmldoc.childNodes #(1) [<DOM Element: grammar at 17538908>] >>> xmldoc.childNodes[0] #(2) <DOM Element: grammar at 17538908> >>> xmldoc.firstChild #(3) <DOM Element: grammar at 17538908>
- Każdy węzeł posiada atrybut
childNodes
, który jest listą obiektówNode
. ObiektDocument
zawsze ma tylko jeden węzeł potomny, element główny (korzeń) dokumentu XML (w tym przypadku elementgrammar
). - Aby dostać się do pierwszego (i w tym wypadku jedynego) węzła potomnego, używamy po prostu zwykłej składni do obsługi list. Pamiętajmy, tu nie dzieje się nic nadzwyczajnego; to jest po prostu zwykła lista Pythona zwykłych pythonowych obiektów.
- Ponieważ pobieranie pierwszego węzła potomnego danego węzła jest bardzo użyteczną i częstą czynnością, klasa
Node
posiada atrybutfirstChild
, który jest synonimem dlachildNodes[0]
. (Jest też atrybutlastChild
, który jest synonimem dlachildNodes[-1]
.)
Przykład. Metoda
toxml
działa w każdym węźle
>>> grammarNode = xmldoc.firstChild
>>> print grammarNode.toxml() #(1)
<grammar>
<ref id="bit">
<p>0</p>
<p>1</p>
</ref>
<ref id="byte">
<p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
<xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
</ref>
</grammar>
- Ponieważ metoda
toxml
jest zdefiniowana w klasieNode
, jest ona dostępna w każdym węźle XML-a, nie tylko w elemencieDocument
.
Przykład. Węzłami potomnymi może być także tekst
>>> grammarNode.childNodes #(1) [<DOM Text node "\n">, <DOM Element: ref at 17533332>, \ <DOM Text node "\n">, <DOM Element: ref at 17549660>, <DOM Text node "\n">] >>> print grammarNode.firstChild.toxml() #(2) >>> print grammarNode.childNodes[1].toxml() #(3) <ref id="bit"> <p>0</p> <p>1</p> </ref> >>> print grammarNode.childNodes[3].toxml() #(4) <ref id="byte"> <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\ <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p> </ref> >>> print grammarNode.lastChild.toxml() #(5)
- Patrząc na XML w kgp/binary.xml, moglibyśmy pomyśleć, że węzeł grammar ma tylko dwa węzły potomne, czyli dwa elementy ref. Ale chyba o czymś zapominamy: o znakach końca linii! Za elementem '<grammar>' i przed pierwszym '<ref>' jest znak końca linii i zalicza się on do węzłów potomnych elementu grammar. Podobnie jest też znak końca linii po każdym '</ref>'; to także zalicza się do węzłów potomnych. Tak więc
grammar.childNodes
jest właściwie listą 5 obiektów: 3 obiektyText
i 2 obiektyElement
. - Pierwszym potomkiem jest obiekt
Text
reprezentujący znak końca linii za znacznikiem '<grammar>' i przed pierwszym '<ref>'. - Drugim potomkiem jest obiekt
Element
reprezentujący pierwszy element ref. - Czwartym potomkiem jest obiekt
Element
reprezentujący drugi element ref. - Ostatnim potomkiem jest obiekt
Text
reprezentujący znak końca linii za znacznikiem końcowym '</ref>' i przed znacznikiem końcowym '</grammar>'.
Przykład. Drążenie aż do tekstu
>>> grammarNode <DOM Element: grammar at 19167148> >>> refNode = grammarNode.childNodes[1] #(1) >>> refNode <DOM Element: ref at 17987740> >>> refNode.childNodes #(2) [<DOM Text node "\n">, <DOM Text node " ">, <DOM Element: p at 19315844>, \ <DOM Text node "\n">, <DOM Text node " ">, \ <DOM Element: p at 19462036>, <DOM Text node "\n">] >>> pNode = refNode.childNodes[2] >>> pNode <DOM Element: p at 19315844> >>> print pNode.toxml() #(3) <p>0</p> >>> pNode.firstChild #(4) <DOM Text node "0"> >>> pNode.firstChild.data #(5) u'0'
- Jak już widzieliśmy w poprzednim przykładzie, pierwszym elementem ref jest
grammarNode.childNodes[1]
, ponieważchildNodes[0]
jest węzłem typuText
dla znaku końca linii. - Element ref posiada swój zbiór węzłów potomnych, jeden dla znaku końca linii, oddzielny dla znaków spacji, jeden dla elementu p i tak dalej.
- Możesz użyć metody
toxml
nawet tutaj, głęboko wewnątrz dokumentu. - Element p ma tylko jeden węzeł potomny (nie możemy tego zobaczyć na tym przykładzie, ale spójrzmy na
pNode.childNodes
jeśli nie wierzymy) i jest nim obiektText
dla pojednyczego znaku'0'
. - Atrybut
.data
węzłaText
zawiera rzeczywisty napis, jaki ten tekstowy węzeł reprezentuje. Zauważmy, że wszystkie dane tekstowe przechowywane są w unikodzie.