Автор — Грэг Валтерс (Greg Walters)
Возможно, вы слышали об XML, однако можете и не знать, что это такое. В этом месяце нашу лекцию мы посвятим XML. Цели лекции:
- познакомить вас с тем, что такое XML;
- показать, как читать и записывать XML-файлы в ваших приложениях;
- подготовить к серьёзному проекту с использованием XML в следующий раз.
Итак, давайте поговорим об XML. XML означает «EXtensible Markup Language» («Расширяемый язык разметки»). Он достаточно похож на HTML. Формат разработан как способ хранения и эффективной передачи данных через интернет или другим образом. XML – это по существу текстовой файл, отформатированный с использованием ваших собственных тэгов и имеющий хорошие средства самодокументирования. Являясь текстом, он может быть сжат для более быстрой и легкой передачи данных. В отличие от HTML, XML сам по себе действий не производит и не связан с визуальным представлением данных. Как я отмечал, XML не требует набора стандартных тэгов: вы можете создавать свои собственные.
Давайте взглянем на типичный пример XML-файла.
<root> <node1> Данные… </node1> <node2 attribute=«something»>Данные Узла 2</node2> <node3> <node3sub1> ещё данные </node3sub1> </node3> </root>
Первое, что обращает на себя внимание, – это отступы. На самом деле они просто облегчают восприятие человеком. XML-файл будет рабочим, даже если он выглядит так:
<root><node1> Данные… </node1><node2 attribute=«something»> Данные Узла 2 </node2><node3><node3sub1>ещё данные</node3sub1></node3></root>
Тэги внутри угловых скобок «< >» отвечают некоторым правилам. Они должны состоять из одного слова. Каждому открывающему тэгу (например, <root>) должен соответствовать закрывающий, начинающийся с «/». Кроме того, тэги чувствительны к регистру: <node>, <Node>, <NODE> и <NodE> – различны, и к каждому из них должен быть свой закрывающий тэг. Наименования тэгов могут содержать буквы, цифры и другие символы, но не могут начинаться с цифры или знака препинания. Избегайте знаков «-», «.», «:» в именах тэгов, так как некоторые программы могут рассматривать их как команды или свойства объекта. Помимо этого, запятые зарезервированы для некоторых других целей. Тэги также можно называть элементами.
Каждый XML-файл – это древовидная структура, начинающаяся с корня и ответвляющаяся от него. Он ДОЛЖЕН иметь корневой элемент – родитель для всех других элементов в файле. Вернёмся к нашему примеру. От корня отходят три дочерних элемента: node1, node2 и node3. Дочерние элементы имеют общий корень, а node3 является родителем для node3sub1.
Рассмотрим второй узел node2. Обратите внимание, что вдобавок к обычной информации внутри угловых скобок содержится атрибут (attribute). В настоящее время многие разработчики избегают использования атрибутов, так как без них элементы эффективнее и удобнее в использовании, но вы обнаружите, что атрибуты до сих пор используются. Мы познакомимся с ними позже.
Ниже приведён полезный пример.
Имеется корневой элемент «people», содержащий два дочерних – «person». Каждый элемент «person» содержит 6 дочерних элементов: «firstname», «lastname», «gender», «address», «city» и «state». На первый взгляд, XML-файл можно рассматривать как базу данных (вспомните несколько последних лекций), и это верное предположение. Некоторые приложения используют XML-файлы как простую структуру базы данных. Не составит труда написать программу для чтения XML-файла. Она должна открыть файл, прочитать его построчно и, в зависимости от элемента, использовать заключённые в нём данные, а затем закрыть файл. Однако есть и более эффективные способы.
В следующем примере мы воспользуемся модулем библиотеки под названием ElementTree. Его можно получить непосредственно из Synaptic, установив python-elementtree. Однако я предпочитаю устанавливать с сайта ElementTree (http://effbot.org/downloads/#elementtree) и непосредственно загружать файл-исходник (elementtree-1.2.6-20050316.tar.gz). После загрузки я с помощью менеджера пакетов извлекаю его во временную папку. Затем в этой папке выполняю «sudo python setup.py install». Эта команда помещает файлы в общую папку python, поэтому в дальнейшем у меня есть возможность использовать модуль и в python 2.5, и в 2.6. Итак, приступим к работе! Создайте папку, которая будет содержать код этого месяца, скопируйте приведённые выше данные в XML-формате в ваш любимый текстовой редактор и сохраните их в эту папку под именем «xmlsample1.xml».
Поговорим о нашем коде. В первую очередь хочется протестировать установленный модуль ElementTree.
import elementtree.ElementTree as ET tree = ET.parse('xmlsample1.xml') ET.dump(tree)
Запустив эту программу, мы получим нечто похожее на то, что представлено ниже.
Всё, что делает программа, – позволяет модулю ElementTree открыть файл, разбить на структурные элементы и сохранить в памяти в таком виде. Ничего сверхъестественного.
Теперь заменим наш код на следующий:
import elementtree.ElementTree as ET tree = ET.parse('xmlsample1.xml') person = tree.findall('.//person') for p in person: for dat in p: print "Элемент: %s - Данные: %s" %(dat.tag,dat.text)
Запустите код снова. Результат будет таким:
/usr/bin/python -u «/home/greg/Documents/articles/xml/reader1.py»
- Элемент: firstname - Данные: Саманта
- Элемент: lastname - Данные: Фэроу
- Элемент: gender - Данные: Женский
- Элемент: address - Данные: ул. Мэйн, д. 123
- Элемент: city - Данные: Дэнвер
- Элемент: state - Данные: Колорадо
- Элемент: firstname - Данные: Стив
- Элемент: lastname - Данные: Левон
- Элемент: gender - Данные: Мужской
- Элемент: address - Данные: Бульвар Арапахо, 332120
- Элемент: city - Данные: Дэнвер
- Элемент: state - Данные: Колорадо
Теперь каждая порция данных выводится напротив имени тэга. Эти данные можно легко распечатать. Итак, посмотрим, что делает программа. Модуль ElementTree проанализировал файл и поместил результаты в объект tree. Затем ElementTree нашёл все вхождения тэга person. В нашем примере таких элементов оказалось два, но их могло быть 1 или 1000. Элемент person – дочерний к корневому элементу people. Все данные были разбиты на порции. Затем мы создали простой цикл, перебирающий объекты person. Вложенный в него цикл перебирает данные каждого элемента person, и на экран выводятся результаты, показывающие имя элемента (.tag) и данные (.text).
Рассмотрим более жизненный пример. Я со своей семьёй играю в Геокэшинг. Для тех, кто не знает, Геокэшинг – это «охота за сокровищами», когда «гики» используют мобильные устройства с GPS, чтобы найти нечто, спрятанное кем-то другим. Они публикуют GPS-координаты на веб-сайте, иногда с подсказками, а другие вводят координаты в свои GPS-навигаторы и пытаются найти тайник. По данным Википедии во всём мире существует более миллиона действующих точек кэшинга, таким образом, вероятно, они найдутся и поблизости от вас. Для поиска таких мест я использую два веб-сайта: http://www.geocaching.com/ и http://navicache.com/. Есть и другие сайты, но эти два – наиболее крупные.
Файлы, которые содержат информацию о каждом месте геокэшинга, – это обычно XML-файлы. Существуют программы, которые получают такие данные и передают их в устройство GPS. Некоторые из них действуют как приложения баз данных – позволяют отслеживать вашу деятельность, иногда с использованием карт. Теперь сконцентрируемся на анализе загружаемых файлов.
Я зашел на Navicache и обнаружил последний добавленный тайник в Техасе. Информация из файла показана на предыдущей странице.
Скопируем данные и сохраним их в файл «Cache.loc». Перед началом составления программы внимательно рассмотрим файл.
Первая строка обычно сообщает, что это проверенный XML-файл, её можно проигнорировать. Следующая строка, начинающаяся с «loc», является корневым элементом и содержит атрибуты «version» и «src». Выше я отмечал, что атрибуты иногда используются в файлах. В дальнейшем мы ещё будем иметь дело с ними в этом файле. Корневой элемент тут также может быть проигнорирован. Следующая строка содержит тэг дочернего элемента – маршрутной точки (waypoint). (Маршрутная точка – это место расположения, где можно найти тайник). Из этого элемента можно получить важные сведения: наименование тайника, координаты (долготу и широту), тип тайника и ссылку на интернет-страницу с дополнительной информацией. Элемент с именем содержит кучу полезной информации, но проводить её структурный анализ придётся самостоятельно. Давайте теперь создадим программу для чтения и отображения файла cache.loc, назвав её readacache.py. Начнём с импорта модуля и команд для структурного анализа из предыдущего примера.
import elementtree.ElementTree as ET tree = ET.parse('Cache.loc')
Нам необходимо получить только данные, находящиеся внутри тэга waypoint. Для этого мы используем функцию .find внутри модуля ElementTree. Результаты работы будут сохранены в объекте w.
w = tree.find('.//waypoint')
Затем нам необходимо просмотреть все данные, для чего используется цикл for. В теле цикла мы ищем элементы name (имя), coord (координаты), type (тип) и link (ссылка). На основании содержащихся в тэге данных, мы извлекаем информацию, чтобы распечатать её в дальнейшем.
for w1 in w: if w1.tag == "name":
Просмотрим, какие данные можно извлечь из тэга name.
<name id="N02CAC"><![CDATA[Возьмите Фотографии озера Виноградной лозы, сделанные g_phillips Тайник открыт: не ограничено Тип тайника: обычный Размер: обычный Сложность: 1.5 Местность: 2.0]]></name>
Это одна очень длинная строка. Идентификатор id установлен как атрибут. Имя тайника – это часть строки после «CDATA» и до «Тайник открыт:». Мы разделим строку на несколько более мелких частей.
newstring = oldstring[startposition:endposition]
Итак, приведенный код можно использовать для извлечения необходимой информации.
Затем нам следует извлечь идентификатор, который содержится в атрибуте id тэга name. Проверим, имеются ли какие-либо атрибуты (а как мы знаем, они имеются), следующим образом:
if w1.keys(): for name,value in w1.items(): if name == 'id': CacheID = value
Перейдём к работе с тэгами координат, типа и ссылки; соответствующий код показан ниже. В итоге мы выведем полученную информацию, используя код, размещенный в нижнем правом углу. Правее – полный листинг программы.
Теперь вы получили достаточно информации, чтобы суметь реализовать код для чтения большинства XML-файлов. Как всегда, вы можете получить полный код этой лекции на моём веб-сайте http://www.thedesignatedgeek.com.
В следующей лекции мы применим наши знания формата XML для получения и вывода на терминал сведений с замечательного сайта погоды. Наслаждайтесь!
Коды:
#1
<people> <person> <firstname>Samantha</firstname> <lastname>Pharoh</lastname> <gender>Female</gender> <address>123 Main St.</address> <city>Denver</city> <state>Colorado</state> </person> <person> <firstname>Steve</firstname> <lastname>Levon</lastname> <gender>Male</gender> <address>332120 Arapahoe Blvd.</address> <city>Denver</city> <state>Colorado</state> </person> </people>
#2
/usr/bin/python -u "/home/greg/Documents/articles/xml/reader1.py" <people> <person> <firstname>Samantha</firstname> <lastname>Pharoh</lastname> <gender>Female</gender> <address>123 Main St.</address> <city>Denver</city> <state>Colorado</state> </person> <person> <firstname>Steve</firstname> <lastname>Levon</lastname> <gender>Male</gender> <address>332120 Arapahoe Blvd.</address> <city>Denver</city> <state>Colorado</state> </person> </people>
#3
<?xml version="1.0" encoding="ISO-8859-1"?> <loc version="1.0" src="NaviCache"> <waypoint> <name id="N02CAC"><![CDATA[Take Goofy Pictures at Grapevine Lake by g_phillips Open Cache: Unrestricted Cache Type: Normal Cache Size: Normal Difficulty: 1.5 Terrain : 2.0]]></name> <coord lat="32.9890166666667" lon="-97.0728833333333" /> <type>Geocache</type> <link text="Cache Details"> http://www.navicache.com/cgi-bin/db/displaycache2.pl ?CacheID=11436</link> </waypoint> </loc>
#4
# Get text of cache name up to the phrase "Open Cache: " CacheName = w1.text[:w1.text.find ("Open Cache: ")-1] # Get the text between "Open Cache: " and "Cache Type: " OpenCache = w1.text[w1.text.find ("Open Cache: ")+12:w1.text.find("Cache Type: ")-1] # More of the same CacheType = w1.text[w1.text.find ("Cache Type: ")+12:w1.text.find ("Cache Size: ")-1] CacheSize = w1.text[w1.text.find ("Cache Size: ")+12:w1.text.find ("Difficulty: ")-1] Difficulty= w1.text[w1.text.find ("Difficulty: ")+12:w1.text.find ("Terrain : ")-1] Terrain = w1.text[w1.text.find ("Terrain : ")+12:]
#5
elif w1.tag == "coord": if w1.keys(): for name,value in w1.items(): if name == "lat": Lat = value elif name == "lon": Lon = value elif w1.tag == "type": GType = w1.text elif w1.tag == "link": if w1.keys(): for name, value in w1.items(): Info = value Link = w1.text print "Cache Name: ",CacheName print "Cache ID: ",CacheID print "Open Cache: ",OpenCache print "Cache Type: ",CacheType print "Cache Size: ",CacheSize print "Difficulty: ", Difficulty print "Terrain: ",Terrain print "Lat: ",Lat print "Lon: ",Lon print "GType: ",GType print "Link: ",Link
#6
import elementtree.ElementTree as ET tree = ET.parse('Cache.loc') w = tree.find('.//waypoint') for w1 in w: if w1.tag == "name": # Get text of cache name up to the phrase "Open Cache: " CacheName = w1.text[:w1.text.find ("Open Cache: ")-1] # Get the text between "Open Cache: " and "Cache Type: " OpenCache = w1.text[w1.text.find ("Open Cache: ")+12:w1.text.find ("Cache Type: ")-1] # More of the same CacheType = w1.text[w1.text.find ("Cache Type: ")+12:w1.text.find ("Cache Size: ")-1] CacheSize = w1.text[w1.text.find ("Cache Size: ")+12:w1.text.find ("Difficulty: ")-1] Difficulty= w1.text[w1.text.find ("Difficulty: ")+12:w1.text.find ("Terrain : ")-1] Terrain = w1.text[w1.text.find ("Terrain : ")+12:] if w1.keys(): for name,value in w1.items(): if name == 'id': CacheID = value elif w1.tag == "coord": if w1.keys(): for name,value in w1.items(): if name == "lat": Lat = value elif name == "lon": Lon = value elif w1.tag == "type": GType = w1.text elif w1.tag == "link": if w1.keys(): for name, value in w1.items(): Info = value Link = w1.text print "Cache Name: ",CacheName print "Cache ID: ",CacheID print "Open Cache: ",OpenCache print "Cache Type: ",CacheType print "Cache Size: ",CacheSize print "Difficulty: ", Difficulty print "Terrain: ",Terrain print "Lat: ",Lat print "Lon: ",Lon print "GType: ",GType print "Link: ",Link print "="*25 print "finished"