Парсинг XML в JavaScript на примере XML-погоды от gismeteo.ru

Вячеслав Гринин, March 20, 2009

Итак, заинтересовала меня тема, как распарсить XML-файл посредством JavaScript. Немного порыскав в поисковиках наткнулся на весьма познавательную статью по этой теме XML в Microsoft Internet Explorer 5.0. Есть у этой статьи маленький недостаток – она посвящена только IE, а мы в нашем блоге твердо решили писать только кроссбраузерный код. Однако, для начала хватило и этой статьи, сначала весь код был написан для IE, и затем была осуществлена безуспешная попытка запустить его также в Opera, Mozilla, Chrome. Но как я уже говорил “не боги горшки обжигают”, все некроссбраузерные баги были успешно найдены и исправлены. Это р-раз.

В качестве подопытного кролика был взят XML-файл, содержащий в себе данные о погоде на ближайшие сутки от Gismeteo.ru. Собственно, открыв эту страницу, и выбрав интересующий вас город, вы можете ниже на той же странице наблюдать содержимое этого XML-файла, и описание его структуры. Это д-два.

Сопоставив первые два пункта уже можно сваять свой собственный парсер XML-документов на JavaScript, однако если вам лень, то читайте статью дальше. В ней я расскажу о том, как провести анализ содержимого XML-файла и отобразить его в том виде, в котором вам хочется. Это т-три.

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

Сразу хочу предупредить, что ниже пойдет теоретическая и практическая часть работы с XML-файлами, и если вам не хочется вникать во все эти подробности, то вы можете сразу перейти в самый конец статьи и по предложенной там ссылке скачать готовый скрипт обработки XML-документа, содержащего прогноз погоды от gismeteo.ru. А теперь теория…

Итак, что же такое XML? Wikipedia утверждает, что:

XML (eXtensible Markup Language — расширяемый язык разметки) — рекомендованный консорциумом W3C язык разметки, представляющий собой свод общих синтаксических правил. XML — текстовый формат, предназначенный для хранения структурированных данных, для обмена информацией между программами, а также для создания на его основе более специализированных языков разметки. Целью создания XML было обеспечение совместимости при передаче структурированных данных между разными системами обработки информации, особенно при передаче таких данных через Интернет.

Визуально структура XML может быть представлена как дерево. Важнейшее обязательное требование состоит в том, что документ должен иметь только один корневой элемент (в нашем случае это элемент MMWEATHER). Это значит, что данные всего документа должны быть расположены между единственным начальным корневым тегом и соответствующим ему конечным тегом.

Остальная часть XML-документа состоит из вложенных элементов, которые могут иметь атрибуты и содержимое. Элемент обычно состоит из пары тегов (открывающего и закрывающего), обрамляющих другие элементы. Открывающий тег состоит из имени элемента в угловых скобках, например, <FORECAST> ; закрывающий тег состоит из того же имени в угловых скобках, но перед именем ещё добавляется косая черта, например, </FORECAST>. Содержимым элемента называется всё, что расположено между открывающим и закрывающим тегами, включая текст и другие вложенные элементы.

Кроме содержания у элемента могут быть атрибуты — пары имя-значение, добавляемые в открывающий тег после названия элемента. Например в случае <PHENOMENA cloudiness=”2″ precipitation=”10″ rpower=”0″ spower=”0″/>
элемент PHENOMENA имеет 4 атрибута cloudiness, precipitation, rpower и spower имеющие соответственно значения 2, 10, 0, 0.

Итак, со структурой XML-файла более-менее разобрались. Осталось только понять, каким образом все это богатство обрабатывать в JavaScript. Собственно, в статье XML в Microsoft Internet Explorer 5.0 можно обо всем этом прочесть, но я намерен вкратце обрисовать ниже основные приемы анализа XML-файлов в контексте поставленной в начале статьи задачи – разбора XML-документа, содержащего прогноз погоды на сутки от gismeteo.

  1. Загрузка XML-документа. Хочу сразу заметить, что загрузка любых файлов из JavaScript возможна при соблюдении одного обязательного условия – загружаемый файл должен быть расположен в том же домене, где и сама страница его загружающая. Поэтому невозможно со страницы, расположенной, например, по адресу http://easy-4-web.ru/samples/gismeteo/ загрузить файл с сайта http://gismeteo.ru. А это означает, что … проблему надо как-то решать. Есть два пути: либо скачать файл с сайта gismeteo.ru на свой домен, либо создать на вашем домене простой PHP-обработчик, который сам будет скачивать файл с другого домена и выдавать его содержимое от своего имени. В предлагаемом мною решении я пошел по второму пути и поэтому чуть ниже приведу вам PHP-скрипт, выполняющий данное действие. А теперь, собственно, о загрузке XML-документов.
    В IE эта операция производится с помощью следующего кода

    xml=new ActiveXObject("Microsoft.XMLDOM");
    xml.async=false;
    xml.load(url);

    После выполнения которого в переменной xml будет храниться обработанный XML-Документ. Конечно, если документ был верно отформатирован. В ином случае будет сгенерировано исключение.
    Во всех остальных браузерах (Opera, Firefox, Chrome) загрузка файлов выглядит иначе:

    xml=new window.XMLHttpRequest();
    xml.open("GET", url, false);
    xml.send("");

    После чего в поле xml.responseXML можно обнаружить обработанный XML-документ. Таким образом, полный текст функции, осуществляющей загрузку XML-документа, будет выглядеть следующим образом:

    function getXMLDocument(url)
    {
        var xml;
        if(window.XMLHttpRequest)
        {
            xml=new window.XMLHttpRequest();
            xml.open("GET", url, false);
            xml.send("");
            return xml.responseXML;
        }
        else
            if(window.ActiveXObject)
            {
                xml=new ActiveXObject("Microsoft.XMLDOM");
                xml.async=false;
                xml.load(url);
                return xml;
            }
            else
            {
                alert("Загрузка XML не поддерживается браузером");
                return null;
            }
    }
  2. Получение коллекции элементов по имени элемента производится использованием метода getElementsByTagName(tagname) объекта XMLDOMDocument. Например, получить массив всех элементов TOWN можно так: xml.getElementsByTagName(“TOWN”). Понятно, что в переменную xml уже должен быть загружен сам XML-документ.
  3. Просмотр коллекции элементов лучше проводить с помощью цикла for, так как итератор nextNode() пригодный для использования в цикле while, увы, не определен для результата, возвращаемого объектом XMLHttpRequest, а значит будет работать только в IE. Вот код для просмотра коллекции элементов циклом for:
    var towns=xml.getElementsByTagName("TOWN");
    if(towns)
    for(var i1=0; i1<towns.length; i1++)
    {
       town=towns[i1];
       // операции над текущим элементом town
    }
  4. Получение атрибутов элемента.
    Вообще, все атрибуты элемента хранятся в его коллекции attributes, однако доступ к ней неудобен, и поэтому я написал простую функцию, которая пробегает по всей коллекции атрибутов и распаковывает ее в удобный для работы массив, позволяющий получить доступ к любому атрибуту через оператор “квадратные скобки”. Вот эта функция:

    function getAttributes(node)
    {
      var ret = new Object();
      if(node.attributes)
      for(var i=0; i<node.attributes.length; i++)
      {
        var attr = node.attributes[i];
        ret[attr.name] = attr.value;
      }
      return ret;
    }

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

Вот ссылка на готовый скрипт парсера XML-погоды. Вам следует только обратить внимание на шаблоны (переменная template) в конце файла index.html. Это объект в JSON-нотации, содержащий в себе следующие поля: “town”, “forecast”, “phenomena”, “pressure”, “temperature”, “wind”, “relwet”, “heat”, “t_forecast”, “t_template”. Первые 8 представляют собой строки, в которых хранятся шаблоны отображения конкретных нюансов прогноза: город, дата прогноза, атмосферные явления, давление, температура, ветер, относительную влажность, комфортную температуру. В HTML-разметку каждого шаблона вставляются конкретные данные прогноза, которые имеют вид соответствующих имен атрибутов, взятых в фигурные скобки. Все имена атрибутов вы можете увидеть внизу страницы Gismeteo.ru. Для некоторых числовых атрибутов существуют также их строковые псевдонимы, получаются они путем прибавления к атрибуту префикса “s”. Абсолютно все используемые в шаблонах атрибуты вы можете увидеть в конце HTML-разметки страницы index.html, в тегах <script>.

И еще два шаблона, используемых в переменной template. Это t_forecast и t_template. Первый задает разметку прогноза для конкретного времени суток, а второй – разметку для всех прогнозов одного города.

Понимаю, что последний абзац получился запутанным, но, надеюсь, взглянув в код, вы все поймете самостоятельно. Если же возникают вопросы, то пишите их в комментариях к посту, Отвечу на все.

Где-то в середине статьи я обещал показать код PHP-Обработчика, скачивающего XML с других сайтов. Код этот вы можете увидеть в архиве с исходниками в файле xml.php. Он короткий и в пояснениях не нуждается.

В тему:

42комментария

Всё бы ничего, но при отключенном яваскрипте будет бяка. Да и просто не очень рациональное расходование ресурсов. По сути дела, что мы должны сделать? Перегнать один XML (файл погоды с Гисметео) в другой (XHTML-часть страницы), а это куда легче делается посредством серверного XSLT (а заодно и шаблоны куда понятнее получаются). Я такое уже делала, только для MODx (впрочем, легко переписывается под любой другой движок): http://habrahabr.ru/blogs/modx/20607/

Ad_Astra, March 21, 2009 9:20 am Reply

У меня на парсинг XML-погоды есть далеко идущие планы написать еще несколько постов. В том числе и про XSLT.
Спасибо за замечание, меня и самого этот скрипт не до конца удовлетворяет, больно громоздок и непонятен.

веб-программист, March 22, 2009 1:59 am Reply

“ссылка на результаты работы”
почему то не работает ссылка ни в одном браузере
??

Евгений, July 2, 2009 2:39 pm Reply

function getXMLDocument(url)

не отрабатывает функция, во всех браузерах пробывал, выполняется самое первое условие. Менял местами.

Евгений, July 2, 2009 4:08 pm Reply

Сорри предыдущее мое сообщение не читать! =), не могу понять как так, сейчас функция отлично работает.

Евгений, July 3, 2009 9:20 am Reply

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

Насчет того, что не будет отображаться у тех, у кого отклучен жабаскрипт: тогда можно весь информер в скрипт заключить и те, у кого JS отключен, его просто не увидят.
к примеру так:

<script type="text/javascript"> document.write('<span>');</script>
информер
<script> document.write('</span>');</script>

Kaktus, July 23, 2009 1:14 am Reply

загрузки файлов в каталог. Но за скрипт спасибо.

Насчет того, что не будет отображаться у тех, у кого отклучен жабаскрипт: тогда можно весь информер в скрипт заключить и те, у кого JS отключен, его просто не увидят.
к примеру так:

<script type="text/javascript"> document.write('<span>');</script>
информер
<script> document.write('</span>');</script>

Kaktus, July 23, 2009 1:15 am Reply

загрузки файлов в каталог. Но за скрипт спасибо.

Насчет того, что не будет отображаться у тех, у кого отклучен жабаскрипт: тогда можно весь информер в скрипт заключить и те, у кого JS отключен, его просто не увидят.
к примеру так:

document.write(”);
информер
document.write(”);

Kaktus, July 23, 2009 1:15 am Reply

P.S. скрипт порезало, с тегом кода коммент не отправляется

Kaktus, July 23, 2009 1:16 am Reply

Это все хорошо, а как вытащить текст, который находится между xml тегами? какие методы для этого использовать?

vitosik, September 10, 2009 6:55 pm Reply

node.text – для Internet Explorer,
node.textContent – для Firefox и Opera.
таким образом, можно написать:
function getXMLNodeText(node)
{
return node.text || node.textContent;
}
и готово!

Вячеслав Гринин, September 10, 2009 10:18 pm Reply

А как быть, если мой хостинг не поддерживает PHP? Можно по-другому?

Максим, September 14, 2009 6:52 pm Reply

К сожалению, JavaScript не умеет делать кросдоменные запросы. Зато этому можно научить Flash-объект. Парсинг XML посредством ActionScript-а я уже описывал вот здесь http://easy4web.ru/?p=458
А чтобы ActionScript научился делать кросдоменные запросы, нужно подложить в ту же папку где лежит сам Flash-объект еще и файл crossdomain.xml вот с такой структурой:
<?xml version=”1.0″?>
<cross-domain-policy>
<allow-access-from domain=”www.friendmydomain” />
<allow-access-from domain=”*.foo.com” />
<allow-access-from domain=”105.216.0.40″ />
</cross-domain-policy>

Вячеслав Гринин, September 15, 2009 8:22 am Reply

Вот, кстати, отличная статья на тему кросдоменных скриптов http://javascript.ru/ajax/cross-domain-scripting
В ней предложены также методы кросдоменного взаимодействия и без использования технологии Flash.

Вячеслав Гринин, September 15, 2009 8:30 am Reply

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

Серж, September 26, 2009 11:42 am Reply

Серж, покажите где вы выложили скрипт, чтобы я мог оценить причину неработоспособности.

Вячеслав Гринин, September 26, 2009 12:42 pm Reply

Я пробую на компе, открываю файл, пусто, ничего не отображается

Серж, September 26, 2009 10:42 pm Reply

А вы просто распаковали скрипт и запустили HTML-файл или все-таки установили web-сервер (например пакет denwer) и запустили файл с сервера (http://localhost/…)?

admin, September 28, 2009 8:08 am Reply

Сейчас делаю такую фичу для своего сайта, спасибо за инфу

Алексей, January 8, 2010 3:09 pm Reply

Поставил на сайте, все отлично работало, через неделю получил Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIXMLHttpRequest.send]
Что это может означать?

Иван, February 15, 2010 11:19 am Reply

Все конечно интересно, но как то не выходит, в xml.responseXML обработанный элемент отсутствует. или что то не так делаю или рецепт не работает.

max, May 29, 2010 12:21 am Reply

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

lego", May 29, 2010 12:23 am Reply

Предлагаю не заморачиваться рецептом, предложенным в данной статье. Рецепт очень устарел с тех пор как был мною написан. Предлагаю пользоваться XSLT-преобразованиями, подробно описанными в статьях:
http://easy4web.ru/?p=479 и
http://easy4web.ru/?p=604 .

admin, May 31, 2010 8:44 am Reply

Для lego напоминаю, то исходный код приведенного метода лежит в файле http://easy4web.ru/images/informer.zip

admin, May 31, 2010 8:45 am Reply

Кривовато малость работает!

max, August 29, 2010 5:37 pm Reply

Я уже давно для обработки XML предлагал использовать XSLT, о чем у меня в блоге есть подробная статья.

А материалы данной статьи рекомендую использовать только в образовательных целях, потому что для использования в реальных проектах это никак не годится.

admin, August 30, 2010 8:43 am Reply

как раз искал подобную вещичку для бесплатного хоста

Рики, November 16, 2010 9:11 pm Reply

#xml без xml
$simple=file_get_contents(“http://informer.gismeteo.ru/xml/код города.xml”);
$simple=eregi_replace(“[\”]”,””,$simple);
$xmldata=explode(“\n”,$simple);$vsego=sizeof($xmldata);
for($i=0;$i<=$vsego;$i++){
$xmldata[$i]=trim($xmldata[$i]);$newkey=explode(" ",$xmldata[$i]);
if(empty($newkey[1]))continue;
$namedir=$newkey[0];
$domain=strstr($xmldata[$i]," ");$domain=eregi_replace(" ","&",$domain);
$domain=parse_str($domain,$arg);$rootkey[$namedir][]=$arg;
}
$pogodagorodname="Название города";
# что вытпскиваем из xml например ниже дату
$pogodaday=$rootkey[FORECAST][0][day];
$pogodaday_1=$rootkey[FORECAST][1][day];
# и так далее

Alexgrom, March 28, 2011 8:36 am Reply

Йа Бяякооо!!! о_О

Бяка, August 3, 2011 9:10 am Reply

Спасибо за парсер, то что искал!

Женя, December 25, 2011 8:02 pm Reply

Если Вам нужно преобразование XML в HTML, то обратите еще внимание на XSLT-преобразование, оно более гибкое и понятное, нежели низкоуровневый JavaScript-парсинг.

Вячеслав Гринин, December 26, 2011 8:43 am Reply

Как вывести ближайшее время суток из прогноза, т.е. всего одно значение, а не 4?

Максим Уполовников, June 13, 2012 12:03 am Reply

В функции parseGismeteoXML уберите цикл for(var i1=0; i1<towns.length; i1++) и вместо него напишите var i1=0

Вячеслав Гринин, June 14, 2012 3:26 pm Reply

Разбирать xml НЕ используя e4x извращение. Проще уже тогда грузить xml на стороне сервера а фронту пусть сервер наш выплевывает json

j-j, July 6, 2012 12:21 am Reply

Согласен, это извращение. Зато приведенный код полностью кроссбраузерный. А как там насчет e4x? Вот со второй частью соглашусь – лучше всего разбирать xml на стороне сервера.

Вячеслав Гринин, July 9, 2012 6:17 am Reply

А как сделать так, как на этом сайте: http://www.db22.ru – у них с гисметео погода парсится, и на несколько дней!

anadikt, January 25, 2013 11:02 pm Reply

Наконец то этот работает, парсер. Спасибо!

Comkin, April 10, 2013 9:28 pm Reply

а как можно сразу несколько дней отображать ???

Султан, October 12, 2013 3:00 pm Reply

ссылка на готовый скрипт парсера
скачал, открыл показывает ошибку

жалгас, January 13, 2014 9:46 am Reply

стоило бы написать еще как открывали, какую ошибку показал скрипт?

Вячеслав Гринин, January 15, 2014 10:43 am Reply

Здравствуйте, смените пожалуйста фон на своем сайте, а то в глазах ребит

Aleksey, April 10, 2014 3:33 pm Reply

И сразу же баг. “Погода в городе Ìîñêâà[27612] “. Хотя бы юникод в файлике поставьте

1, July 16, 2015 12:50 pm Reply
Ваше имя
Ваш email*
Ваш сайт
Текст вашего комментария:

Поиск по блогу:
Подписаться:
Популярные:
Облако тегов:
Разное:
Счетчик: