Парсинг XML с помощью PHP скрипта

15 июня, 2008

Логотип статьи об обработке XML

На сегодняшний день отправка данных в формате XML получила очень широкое распространение. Взять хотя бы RSS ленты. Переоценить их значение очень сложно, а ведь по-сути это обычные XML файлы с данными.

Основное преимущество этого способа передачи данных в том, что можно с минимальными усилиями написать обработчик данных и использовать их в своих приложениях. Например, работа практически всех мешапов (mashup) так или иначе, связана с использованием этого формата.

В этой статье я постараюсь показать, что работать с форматом XML действительно не сложно, а польза от его использования огромна.

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

Ясно, что нанимать человека, который будет слушать прогнозы погоды и обновлять данные совсем не интересно. Тем более, что существуют службы, которые предоставляют эту информацию.

Например, gismeteo.ua. Эта служба предоставляет готовые информеры и XML файлы с данными для каждого города.

Подключить информер не сложнее чем обычный баннер, т.е. нужно добавить их HTML код на страницу. Но использование XML данных дает ряд преимуществ.

Во-первых, вы сможете показать данные в том виде, который больше соответствует дизайну сайта.

Во-вторых, такие данные можно легко сохранить и показывать историю изменения погоды за какой-то период, сравнивать данные по городам и т.д. Вариантов тут масса.

Теперь разберем, как получить и обработать эти данные с помощью PHP.

Прежде всего, нужно четко представлять структуру данных. Для этого заходим на эту страницу и читаем описание.

Здесь я приведу только его фрагмент.

TOWN информация о пункте прогнозирования:
- index уникальный пятизначный код города
- sname закодированное название города
- latitude широта в целых градусах
- longitude долгота в целых градусах
FORECAST информация о сроке прогнозирования:
- day, month, year дата, на которую составлен прогноз в данном блоке
- hour местное время, на которое составлен прогноз
- tod время суток, для которого составлен прогноз: 0 - ночь 1 - утро, 2 - день, 3 – вечер
- weekday день недели, 1 - воскресенье, 2 - понедельник, и т.д.
- predict заблаговременность прогноза в часах
TEMPERATURE температура воздуха, в градусах Цельсия

Жирным шрифтом выделены названия XML элементов, а курсивом – названия их атрибутов.

Сокращенный фрагмент XML файла выглядит так:

  1. <MMWEATHER>
  2. <REPORT type="frc3">
  3.     <TOWN index="33345" sname="%CA%E8%E5%E2" latitude="50" longitude="30">
  4.     <FORECAST day="14" month="6" year="2008" hour="21" tod="3" predict="18" weekday="7">
  5.         <PHENOMENA cloudiness="3" precipitation="4" rpower="1" spower="0"/>
  6.         <PRESSURE max="739" min="737"/>
  7.         <TEMPERATURE max="21" min="19"/>
  8.         <WIND min="2" max="5" direction="7"/>
  9.         <RELWET max="91" min="86"/>
  10.         <HEAT min="19" max="21"/>
  11.     </FORECAST>
  12.     </TOWN>
  13. </REPORT>
  14. </MMWEATHER>

Как видите данные достаточно легко прочитать даже визуально.

Переходим к обработке. Для формата XML существует множество библиотек, которые позволяют получать данные с помощью всего нескольких строк кода. Мы используем стандартную библиотеку PHP.

Принцип ее работы достаточно простой. Вы создаете парсер (обработчик), передаете ему данные в формате XML и устанавливаете функции-обработчики. После этого запускаете обработку. Парсер просматривает данные и вызывает функции-обработчики для каждого найденного элемента (если они установлены, конечно).

Теперь посмотрим, как может выглядеть такой скрипт.

  1. <?php
  2. $res = ";
  3.  
  4. function startElement($parser, $name, $attrs) {
  5.     global $res;
  6.     switch ($name) {
  7.         case 'TOWN':
  8.             $res .= 'Город - ';
  9.             $res .= '<strong>'.mb_convert_encoding(
  10.                         urldecode($attrs['SNAME']),
  11.                         'UTF-8', 'windows-1251').'</strong><br />';
  12.             $res .= 'широта - '.$attrs['LATITUDE'].' градусов<br />';
  13.             $res .= 'долгота - '.$attrs['LONGITUDE'].' градусов<br />';
  14.             break;
  15.         case 'FORECAST':
  16.             $res .= 'Температура '.$attrs['DAY'].'.'.$attrs['MONTH'].'.'.
  17.                 $attrs['YEAR'].' в '.$attrs['HOUR'].'-00 будет от ';
  18.             break;
  19.         case 'TEMPERATURE':
  20.             $res .= '<strong>'.$attrs['MIN'].'°</strong> до <strong>'.
  21.                 $attrs['MAX'].'°</strong><br />';
  22.             break;
  23.     }
  24. }
  25.  
  26. function endElement($parser, $name) {}
  27.  
  28. $ch = curl_init();
  29.  
  30. curl_setopt($ch, CURLOPT_URL, 'http://informer.gismeteo.ua/xml/33345_1.xml');
  31. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  32. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  33. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  34. curl_setopt($ch, CURLOPT_HEADER, 0);
  35. curl_setopt($ch, CURLOPT_PROXYPORT, 8080);
  36. curl_setopt($ch, CURLOPT_PROXY, '192.168.0.1');
  37.  
  38. $data = curl_exec($ch);
  39.  
  40. curl_close($ch);
  41.  
  42. $XMLparser = xml_parser_create();
  43. xml_set_element_handler($XMLparser, 'startElement', 'endElement');
  44. if (!xml_parse($XMLparser, $data)) {
  45.     die('Ошибка обработки данных');
  46. }
  47. xml_parser_free($XMLparser);
  48. ?>
  49. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  50. <html xmlns="http://www.w3.org/1999/xhtml">
  51. <head>
  52. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  53. <title>Данные от gismeteo</title>
  54. </head>
  55. <body style="font-family:Verdana, sans-serif">
  56. <?php
  57. echo $res;
  58. ?>
  59. </body>
  60. </html>

Чтобы немного его упростить данные будем выводить информацию в виде обычной строки (переменная $res).

В начале скрипта мы объявили две функции (startElement и endElement). К ним мы еще вернемся, а сейчас рассмотрим получение данных.

Для этой цели я использовал библиотеку cURL (строки 28-40).

Примечание. Возможно, это и перебор, т.к. получить файл можно с помощью функции fopen(). Но она не работает с прокси, и без cURL пришлось бы использовать сокеты (пример готового скрипта есть в комментариях к этой статье).

В общем, способ получения файла выбираете по своему желанию, а мы переходим к обработке.

Для этого с помощью функции xml_parser_create() создаем XML парсер (строка 42) и затем устанавливаем обработчики элементов (строка 43). Обработчиков у нас два. Первый - startElement будет вызван, когда парсер найдет открывающий тег. Второй - endElement будет вызван для каждого закрывающего тега (этот обработчик я привел только в качестве примера, он ничего не делает, и его можно было опустить).

Формирование данных осуществляет функция startElement. Когда парсер находит очередной элемент, он передает ей его название и массив с атрибутами.

С помощью оператора switch (строки 6-23) мы определяем, какой элемент найден и формируем строку с данными.

Запуск обработки выполняется функцией xml_parse, которой нужно передать парсер и данные из XML файла. Эта функция возвратит TRUE, если обработка пройдет без ошибок.

После окончания работы с парсером его нужно удалить с помощью функции xml_parser_free (строка 47).

В строках 48-60 мы формируем HTML страницу и выводим данные о погоде (строка 57). Результат выполнения скрипта показан на скриншоте.

Скриншот прогноза погоды

Как видите, работать с парсером не сложно, главное правильно написать обработчики.

Удачи!

Понравилась статья? Подпишитесь на продолжение rss link !

]]>

Добавьте эту страницу в google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru

]]>

Опубликовано в PHP, Web разработка

]]>

Комментарии (31)

Вы можете отслеживать обсуждение записи с помощью RSS 2.0 rss link

Вы также можете оставить комментарий, или трекбек с Вашего сайта.

]]>
  1. Как по мне - лучше использовать SimpleXML…

    • По-моему, SimpleXML должен потреблять больше ресурсов, т.к. создает объект с деревом.
      XML Parser читает документ один раз и вызывает обработчики. Конечно, это накладывает свои ограничения, например, нельзя вернуться к предыдущему элементу и нельзя изменить сам xml документ, но если нужно просто найти какие-нибудь данные в документе, то зачем тратить ресурсы?

  2. Ну если надо лишь вытащить чуть-чуть данных, то и создавать XML парсер нет необходимости - можно регуляркой (иль strpos) выщемить кусочек необходимых данных…

    • Согласен, но все-таки с помощью библиотеки удобнее. Переменная с именем элемента, массив с атрибутами, ну и т.д.

  3. Спасибо,я использую метод с fopen, но столкнулся с такой проблемой: как сграбить погоду для нескольких дат сразу и вывести их?
    например:

  4. пример не влез в комментарий, ну вобщем если несколько FORECAST нужно сграбить

    • Для этого нужен xml файл с прогнозом на несколько дат, а его они не дают. В файле находится 4 блока FORECAST (утро, день, вечер, ночь). Например, если вы запросите файл 16 числа после обеда, то в нем будет прогноз на вечер 16-ого, ночь, утро и день 17-ого.
      Их готовые информеры тоже не показывают долгосрочных прогнозов. Видимо, они хотят чтобы посетители чаще к ним заходили :-)

  5. MAX

    Простите за то что не в тему, но скопированный код в файл php не работает. Ошибка в строке 9 (по видимому не нравятся ковычки). Подскажите, что нужно изменить

    • Наверное, кавычки :-)
      Должны быть одинарные или двойные. Любой редактор с подсветкой синтаксиса должен подсветить все, что находится между ними как текст.

      P.S. Если не получится, пишите, вышлю скрипт почтой.

  6. MAX

    Вышлите, пожалуйста glebn[at]tut.by
    Спасибо.

  7. Благодарю. Полезная статейка. Сам пользуюсь методом с fopen, сейчас подкорректировал некоторые недочеты по статье.

  8. Матерьяльчик интересный. Но я пользуюсь СимплХМЛ

  9. 2Gorant Это как? Что это такое?

  10. Gorant, а разве с его помощью можно такое сделать?

    • Да, можно. Фактически SimpleXML имеет большие возможности, т.к. позволяет изменять документ. Просто отличается принцип работы.
      После парсинга SimpleXML возвращает объект с деревом, которое содержит xml документ. Вы можете получить доступ к любому элементу этого дерева, обойти их в цикле, изменить и т.д.

  11. Я предпочту регулярки при маленьких работах.
    Ну а для большого дело вполне возможно и это применять)

  12. Nik

    Добрый день Владимир, Спасибо за разбор парсинга. Но к сожалению у меня тоже вылазит ошибка в 9 строке. Не могли бы Вы скинуть код на мыло 1452(соббака)list.ru Заранее благодарю!

  13. Парсинг через xml_parser быстрее, так как он поточный, но от этого и его недостаток в плане удобства использования. Для небольших скриптов вроде этого можно использовать и объектную модель вроде simplexml. Хотя есть еще несколько способов, например XQuery:http://i-novice.net/poisk-v-xml-s-pomoshhyu-xquery/
    или XPath : http://i-novice.net/phpxmlxpath-chast-2/.

  14. Николай

    Подскажите пожалуйста что нужно поменять в парсере, чтобы выводились данные конкретных элементов без атрибутов. Например как вывести только название из тега вот из такого файла

    title 2
    news text 2

  15. Николай

    все теги вырезались((( как вывести название из тега
    title 2
    news text 2

    • Если кратко - смотрите функцию xml_set_character_data_handler.

      Полный пример можно посмотреть здесь.

      Честно говоря, использовать XML Parser для работы с содержимым тегов не очень удобно. Если xml-файл не очень большой попробуйте SimpleXML. Она потребляет больше ресурсов, но код получается короче.

  16. zdfb

    эти xml классы гребанные не везде работают осторжнее с ними
    таки вот ошибки из за них

    Call-time pass-by-reference has been deprecated

    которые могут запросто повесить сайт

    • Такие ошибки могут возникнуть не только с этими классами. Настройками в php.ini можно сделать невозможной работу практически любого скрипта.

      В любом случае очень желательно установить локальный сервер с такими же настройками как и у хостера. Тогда можно будет проверить скрипт до переноса на сервер.

  17. Привет. А у меня чего-то не получается.
    Чего-то не понимаю что к чему.
    Не поможете?
    Вот что апач пишет:

    [error] PHP Notice: Undefined variable: res in /usr/local/www/weather/index2.php on li
    ne 2
    [error] PHP Parse error: syntax error, unexpected T_ENCAPSED_AND_WHITESPACE, expecting
    T_STRING or T_VARIABLE or T_NUM_STRING in /usr/local/www/weather/index2.php on line 11

    • Вы скопировали код со страницы?
      Во второй строке должны быть две одинарные кавычки, а не одна двойная.
      В 11-ой - работа с функцией mb_convert_encoding. Должно быть подключено расширение mbstring (в php.ini).

      • Уже разобрался.
        Спасибо!

        Полностью разобрал по примеру все дерево. Теперь думаю как переложить на сервер, сняв с клиента.

  18. интересно, а как оно будет работать с невалидным хмл?

]]>

Оставить комментарий

* - обязательные для заполнения поля

]]>