HOW-TO: Программа на Python, Часть 10 Сравнение версий

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Следующая версия
Предыдущая версия
fullcircle:36:python_ч_10 [2010/08/07 20:53]
создано
fullcircle:36:python_ч_10 [2011/02/20 16:27] (текущий)
Строка 3: Строка 3:
 //​Автор — Грэг Валтерс (Greg Walters)// //​Автор — Грэг Валтерс (Greg Walters)//
 </​style>​ </​style>​
 +
  
   - [[..:​27:​python_ч_1|Программа на Python — часть 1]]   - [[..:​27:​python_ч_1|Программа на Python — часть 1]]
Строка 15: Строка 16:
   - [[..:​36:​python_ч_10|Программа на Python — часть 10]]   - [[..:​36:​python_ч_10|Программа на Python — часть 10]]
   - [[..:​37:​python_ч_11|Программа на Python — часть 11]]   - [[..:​37:​python_ч_11|Программа на Python — часть 11]]
 +  - [[..:​38:​python_ч_12|Программа на Python — часть 12]]
 +  - [[..:​39:​python_ч_13|Программа на Python — часть 13]]
 +  - [[..:​40:​python_ч_14|Программа на Python — часть 14]]
 +
 +Возможно,​ вы слышали об XML, однако можете и не знать, что это такое. В этом месяце нашу лекцию мы посвятим XML. Цели лекции:​
 +
 +  * познакомить вас с тем, что такое XML;
 +  * показать,​ как читать и записывать XML-файлы в ваших приложениях;​
 +  * подготовить к серьёзному проекту с использованием XML в следующий раз.
 +
 +Итак, давайте поговорим об XML. XML означает «EXtensible Markup Language» («Расширяемый язык разметки»). Он достаточно похож на HTML. Формат разработан как способ хранения и эффективной передачи данных через интернет или другим образом. XML – это по существу текстовой файл, отформатированный с использованием ваших собственных тэгов и имеющий хорошие средства самодокументирования. Являясь текстом,​ он может быть сжат для более быстрой и легкой передачи данных. В отличие от HTML, XML сам по себе действий не производит и не связан с визуальным представлением данных. Как я отмечал,​ XML не требует набора стандартных тэгов: вы можете создавать свои собственные.
 +
 +Давайте взглянем на типичный пример XML-файла.
 +<​code>​
 +<​root>​
 + <​node1>​ Данные… </​node1>​
 + <node2 attribute=«something»>​Данные Узла 2</​node2>​
 + <​node3>​
 + <​node3sub1>​ ещё данные </​node3sub1>​
 + </​node3>​
 +</​root>​
 +</​code>​
 +Первое,​ что обращает на себя внимание,​ – это отступы. На самом деле они просто облегчают восприятие человеком. 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.
 +
 +<​code>​import elementtree.ElementTree as ET
 +
 +tree = ET.parse('​xmlsample1.xml'​)
 +
 +ET.dump(tree)</​code>​
 +
 +Запустив эту программу,​ мы получим нечто похожее на то, что представлено ниже.
 +
 +Всё, что делает программа,​ – позволяет модулю ElementTree открыть файл, разбить на структурные элементы и сохранить в памяти в таком виде. Ничего сверхъестественного.
 +
 +Теперь заменим наш код на следующий:​
 +
 +<​code>​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)</​code>​
 +
 +Запустите код снова. Результат будет таким:
 +
 +**/​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. Начнём с импорта модуля и команд для структурного анализа из предыдущего примера.
 +
 +<​code>​import elementtree.ElementTree as ET
 +
 +tree = ET.parse('​Cache.loc'​)</​code>​
 +
 +Нам необходимо получить только данные,​ находящиеся внутри тэга waypoint. Для этого мы используем функцию .find внутри модуля ElementTree. Результаты работы будут сохранены в объекте w.
 +
 +<​code>​w = tree.find('​.//​waypoint'​)</​code>​
 +
 +Затем нам необходимо просмотреть все данные,​ для чего используется цикл for. В теле цикла мы ищем элементы name (имя), coord (координаты),​ type (тип) и link (ссылка). На основании содержащихся в тэге данных,​ мы извлекаем информацию,​ чтобы распечатать её в дальнейшем.
 +
 +<​code>​for w1 in w:
 + if w1.tag == "​name":</​code>​
 +
 +Просмотрим,​ какие данные можно извлечь из тэга name.
 +<​code>​
 +<name id="​N02CAC"><​![CDATA[Возьмите Фотографии озера ​
 +Виноградной лозы, сделанные g_phillips
 +
 +Тайник открыт:​ не ограничено
 +
 +Тип тайника:​ обычный
 +
 +Размер:​ обычный
 +
 +Сложность:​ 1.5
 +
 +Местность:​ 2.0]]></​name>​
 +</​code>​
 +Это одна очень длинная строка. Идентификатор id установлен как атрибут. Имя тайника – это часть строки после «CDATA» и до «Тайник открыт:​». Мы разделим
 +строку на несколько более мелких частей. ​
 +
 +<​code>​newstring = oldstring[startposition:​endposition]</​code>​
 +
 +Итак, приведенный код можно использовать для извлечения необходимой информации.
 +
 +Затем нам следует извлечь идентификатор,​ который содержится в атрибуте id тэга name. Проверим,​ имеются ли какие-либо атрибуты (а как мы знаем, они имеются),​ следующим образом:​
 +
 +<​code>​if w1.keys():
 + for name,value in w1.items():
 + if name == '​id':​
 + CacheID = value</​code>​
 +
 +Перейдём к работе с тэгами координат,​ типа и ссылки;​ соответствующий код показан ниже. В итоге мы выведем полученную информацию,​ используя код, размещенный в нижнем правом углу. Правее – полный листинг программы.
 +
 +Теперь вы получили достаточно информации,​ чтобы суметь реализовать код для чтения большинства XML-файлов. Как всегда,​ вы можете получить полный код этой лекции на моём веб-сайте http://​www.thedesignatedgeek.com.
 +
 +В следующей лекции мы применим наши знания формата XML для получения и вывода на терминал сведений с замечательного сайта погоды. Наслаждайтесь!
 +
 +---------------------------------------
 +
 +=====Коды:​=====
 +**#1**
 +<​code><​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>​
 +</​code>​
 +
 +**#2**
 +<​code>/​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></​code>​
 +
 +**#3**
 +<​code>​
 +<?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>​
 +</​code>​
 +
 +**#4**
 +<​code>​
 +# 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:​]
 +</​code>​
 +
 +**#5**
 +<​code>​
 +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
 +</​code>​
 +
 +**#6**
 +<​code>​
 +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"​
 +</​code>​
 +
 +
 +---------------------------------------
 +
 +<style center>
 +//​[[..:​36|К содержанию номера]]//​
 +
 +//​[[:​fullcircle|К архиву журналов]]//​
 +</​style>​
 +
 +{{tag>​howto Full_Circle}}