Язык программирования Python.
Язык XML.
В рамках одной лекции довольно сложно объяснить, что такое XML, и то, как с ним работать. В примерах используется входящий в стандартную поставку пакет xml.
XML (Extensible Markup Language, расширяемый язык разметки) позволяет налаживать взаимодействие между приложениями различных производителей, хранить и подвергать обработке сложно структурированные данные.
Язык XML (как и HTML) является подмножеством SGML, но его применения не ограничены системой WWW. В XML можно создавать собственные наборы тегов для конкретной предметной области. В XML можно хранить и подвергать обработке базы данных и знаний, протоколы взаимодействия между объектами, описания ресурсов и многое другое.
Новичкам не всегда понятно, зачем нужно использовать такой достаточно многословный формат, когда можно создать свой, компактный формат для хранения тех же самых данных. Преимущество XML состоит в том, что вместе с данными он хранит и контекстную информацию: теги и их атрибуты имеют имена. Немаловажно также, что XML сегодня — единый общепринятый стандарт, для которого создано немало инструментальных средств.
Говоря об XML, надо иметь в виду, что XML–документы бывают формально–правильными (well–formed) и состоятельными (valid). Состоятельный XML–документ — это формально–правильный XML–документ, имеющий объявление типа документа (DTD, Document Type Definition). Объявление типа документа задает грамматику, которой текст документа на XML должен удовлетворять. Для простоты изложения здесь не будет рассматриваться DTD, предпочтительнее ограничиться формально–правильными документами.
Для представления букв и других символов XML использует Unicode, что сокращает проблемы с представлением символов различных алфавитов. Однако это обстоятельство необходимо помнить и не употреблять в XML восьмибитную кодировку (во всяком случае, без явного указания).
Следующий пример достаточно простого XML–документа дает представление об этом формате (файл expression.xml):
<?xml version="1.0" encoding="iso–8859–1"?>
<expression>
<operation type="+">
<operand>2</operand>
<operand>
<operation type="*">
<operand>3</operand>
<operand>4</operand>
</operation>
</operand>
</operation>
</expression>
XML–документ всегда имеет структуру дерева, в корне которого сам документ. Его части, описываемые вложенными парами тегов, образуют узлы. Таким образом, ребра дерева обозначают «непосредственное вложение». Атрибуты тега можно считать листьями, как и наиболее вложенные части, не имеющие в своем составе других частей. Получается, что документ имеет древесную структуру.
Примечание: Следует заметить, что в отличие от HTML, в XML одиночные (непарные) теги записываются с косой чертой: <BR/>, а атрибуты — в кавычках. В XML имеет значение регистр букв в названиях тегов и атрибутов.Формирование XML–документа.
Концептуально существуют два пути обработки XML–документа: последовательная обработка и работа с объектной моделью документа.
В первом случае обычно используется SAX (Simple API for XML, простой программный интерфейс для XML). Работа SAX заключается в чтении источников данных (input source) XML–анализаторами (XML–reader) и генерации последовательности событий (events), которые обрабатываются объектами–обработчиками (handlers). SAX дает последовательный доступ к XML–документу.
Во втором случае анализатор XML строит DOM (Document Object Model, объектная модель документа), предлагая для XML–документа конкретную объектную модель. В рамках этой модели узлы DOM–дерева доступны для произвольного доступа,а для переходов между узлами предусмотрен ряд методов.
Можно применить оба этих подхода для формирования приведенного выше XML–документа.
С помощью SAX документ сформируется так:
Import sys.
From xml.sax.saxutils import XMLGenerator.
G = XMLGenerator(sys.stdout).
G.startDocument().
G.startElement("expression", {}).
G.startElement("operation", {"type": "+"}).
G.startElement("operand", {}).
G.characters("2").
G.endElement("operand").
G.startElement("operand", {}).
G.startElement("operation", {"type": "*"}).
G.startElement("operand", {}).
G.characters("3").
G.endElement("operand").
G.startElement("operand", {}).
G.characters("4").
G.endElement("operand").
G.endElement("operation").
G.endElement("operand").
G.endElement("operation").
G.endElement("expression").
G.endDocument().
Построение дерева объектной модели документа может выглядеть, например, так:
From xml.dom import minidom.
Dom = minidom.Document().
E1 = dom.createElement("expression").
Dom.appendChild(e1).
P1 = dom.createElement("operation").
P1.setAttribute('type', '+').
X1 = dom.createElement("operand").
X1.appendChild(dom.createTextNode("2")).
P1.appendChild(x1).
E1.appendChild(p1).
P2 = dom.createElement("operation").
P2.setAttribute('type', '*').
X2 = dom.createElement("operand").
X2.appendChild(dom.createTextNode("3")).
P2.appendChild(x2).
X3 = dom.createElement("operand").
X3.appendChild(dom.createTextNode("4")).
P2.appendChild(x3).
X4 = dom.createElement("operand").
X4.appendChild(p2).
P1.appendChild(x4).
Print dom.toprettyxml().
Легко заметить, что при использовании SAX команды на генерацию тегов и других частей выдаются последовательно, а вот построение одной и той же DOM можно выполнять различными последовательностями команд формирования узла и его соединения с другими узлами.
Конечно, указанные примеры носят довольно теоретический характер, так как на практике строить XML–документы таким образом обычно не приходится.
Анализ XML–документа.
Для работы с готовым XML–документом нужно воспользоваться XML–анализаторами. Анализ XML–документа с порождением объекта класса Document происходит всего в одной строчке, с помощью функции parse(). Здесь стоит заметить, что кроме стандартного пакета xml можно поставить пакет PyXML или альтернативные коммерческие пакеты. Тем не менее, разработчики стараются придерживаться единого API, который продиктован стандартом DOM Level 2:
Import xml.dom.minidom.
Dom = xml.dom.minidom.parse("expression.xml").
Dom.normalize().
Def output_tree(node, level=0):
if node.nodeType == node.TEXT_NODE:
if node.nodeValue.strip():
print ". "*level, node.nodeValue.strip().
else: # ELEMENT_NODE или DOCUMENT_NODE.
atts = node.attributes or {}.
att_string = ", ".join(
["%s=%s " % (k, v) for k, v in atts.items()]).
print ". "*level, node.nodeName, att_string.
for child in node.childNodes:
output_tree(child, level+1).
Output_tree(dom).
В этом примере дерево выводится с помощью определенной функции output_tree(), которая принимает на входе узел и вызывается рекурсивно для всех вложенных узлов.
В результате получается примерно следующее:
#document.
. expression.
. . operation type=+
. . . operand.
. . . . 2.
. . . operand.
. . . . operation type=*
. . . . . operand.
. . . . . . 3.
. . . . . operand.
. . . . . . 4.
Здесь же применяется метод normalize() для того, чтобы все текстовые фрагменты были слиты воедино (в противном случае может следовать подряд несколько узлов с текстом).
Можно заметить, что даже в небольшом примере использовались атрибуты узлов: node.nodeType указывает тип узла, node.nodeValue применяется для доступа к данным, node.nodeName дает имя узла (соответствует названию тега), node.attributes дает доступ к атрибутам узла. node.childNodes применяется для доступа к дочерним узлам. Этих свойств достаточно, чтобы рекурсивно обойти дерево.
Все узлы являются экземплярами подклассов класса Node. Они могут быть следующих типов:
| Название | Описание | Метод для создания |
|---|---|---|
| ELEMENT_NODE | Элемент | createElement(tagname) |
| ATTRIBUTE_NODE | Атрибут | createAttribute(name) |
| TEXT_NODE | Текстовый узел | createTextNode(data) |
| CDATA_SECTION_NODE | Раздел | CDATA |
| ENTITY_REFERENCE_NODE | Ссылка на сущность | |
| ENTITY_NODE | Сущность | |
| PROCESSING_INSTRUCTION_NODE | Инструкция по обработке | createProcessingInstruction(target, data) |
| COMMENT_NODE | Комментарий | createComment(comment) |
| DOCUMENT_NODE | Документ | |
| DOCUMENT_TYPE_NODE | Тип документа | |
| DOCUMENT_FRAGMENT_NODE | Фрагмент документа | |
| NOTATION_NODE | Нотация |
В DOM документ является деревом, в узлах которого стоят объекты нескольких возможных типов. Узлы могут иметь атрибуты или данные. Доступ к узлам можно осуществлять через атрибуты вроде childNodes (дочерние узлы), firstChild (первый дочерний узел), lastChild (последний дочерний узел), parentNode (родитель), nextSibling (следующий брат), previousSibling (предыдущий брат).
Выше уже говорилось о методе appendChild(). К нему можно добавить методы insertBefore(newChild, refChild) (вставить newChild до refChild), removeChild(oldChild) (удалить дочерний узел), replaceChild(newChild, oldChild) (заметить oldChild на newChild). Есть еще метод cloneNode(deep), который клонирует узел (вместе с дочерними узлами, если задан deep=1).
Узел типа ELEMENT_NODE, помимо перечисленных методов «просто» узла, имеет много других методов. Вот основные из них:
TagName.
Имя типа элемента.
GetElementsByTagName(tagname).
Получает элементы с указанным именем tagname среди всех потомков данного элемента.
GetAttribute(attname).
Получить значение атрибута с именем attname.
GetAttributeNode(attrname).
Возвращает атрибут с именем attrname в виде объекта–узла.
RemoveAttribute(attname).
Удалить атрибут с именем attname.
RemoveAttributeNode(oldAttr).
Удалить атрибут oldAttr (задан в виде объекта–узла).
SetAttribute(attname, value).
Устанавливает значение атрибута attname равным строке value.
SetAttributeNode(newAttr).
Добавляет новый узел–атрибут к элементу. Старый атрибут заменяется, если имеет то же имя.
Здесь стоит заметить, что атрибуты в рамках элемента повторяться не должны. Их порядок также не важен с точки зрения информационной модели XML.
В качестве упражнения предлагается составить функцию, которая будет вычислять значение выражения, заданного в XML–представлении.
Пространства имен.
Еще одной интересной особенностью XML, о которой нельзя не упомянуть, являются пространства имен. Они позволяют составлять XML–документы из кусков различных схем. Например, таким образом в XML–документ можно включить кусок HTML, указав во всех элементах HTML принадлежность особому пространству имен.
Следующий пример XML–кода показывает синтаксис пространств имен (файл foaf.rdf):
<?xml version="1.0" encoding="UTF–8"?>
<rdf:RDF.
xmlns:dc="http://http://purl.org/dc/elements/1.1/".
xmlns:rdfs="http://www.w3.org/2000/01/rdf–schema#".
xmlns:foaf="http://xmlns.com/foaf/0.1/".
xmlns:rdf="http://www.w3.org/1999/02/22–rdf–syntax–ns#">
<rdf:Description rdf:nodeID="_:jCBxPziO1">
<foaf:nick>donna</foaf:nick>
<foaf:name>Donna Fales</foaf:name>
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
</rdf:Description>
</rdf:RDF>
Примечание: Пример позаимствован из пакета cwm, созданного командой разработчиков во главе с Тимом Бернерс–Ли, создателем технологии WWW. Кстати, cwm тоже написан на Python. Пакет cwm служит обработчиком данных общего назначения для семантической сети — новой идеи, продвигаемой Тимом Бернерс–Ли. Коротко суть идеи состоит в том, чтобы сделать современный «веб» много полезнее, формализовав знания в виде распределенной базы XML–документов, по аналогии с тем как WWW представляет собой распределенную базу документов. Отличие глобальной семантической сети от WWW в том, что она даст машинам возможность обрабатывать знания, делая логические выводы на основании заложенной в документах информации.Названия пространств имен следуют в виде префиксов к названиям элементов. Эти названия — не просто имена. Они соответствуют идентификаторам, которые должны быть заданы в виде URI (Universal Resource Locator, универсальный указатель ресурса). В примере выше упоминаются пять пространств имен ( xmlns, dc, rdfs, foaf и rdf), из которых только первое не требует объявления, так как является встроенным. Из них реально использованы только три: ( xmlns, foaf и rdf).
Пространства имен позволяют выделять из XML–документа части, относящиеся к различным схемам, что важно для тех инструментов, которые интерпретируют XML.
В пакете xml есть методы, понимающие механизм пространств имен. Обычно такие методы и атрибуты имеют в своем имени буквы NS.
Получить URI, который соответствует пространству имен данного элемента, можно с помощью атрибута namespaceURI.
В следующем примере печатается URI элементов:
Import xml.dom.minidom.
Dom = xml.dom.minidom.parse("ex.xml").
Def output_ns(node):
if node.nodeType == node.ELEMENT_NODE:
print node.nodeName, node.namespaceURI.
for child in node.childNodes:
output_ns(child).
Output_ns(dom).
Пример выведет:
Rdf:RDF http://www.w3.org/1999/02/22–rdf–syntax–ns#
Rdf:Description http://www.w3.org/1999/02/22–rdf–syntax–ns#
Foaf:nick http://xmlns.com/foaf/0.1/
Foaf:name http://xmlns.com/foaf/0.1/
Rdf:type http://www.w3.org/1999/02/22–rdf–syntax–ns#
Следует заметить, что указание пространства имен может быть сделано для имен не только элементов, но и атрибутов.
Подробнее узнать о работе с пространствами имен в xml–пакетах для Python можно из документации.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.