Прогноз погоды для сайта через API Яндекса

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


Итак, начнем сразу с простейшего примера, состоящего из двух файлов: weather.php для описания функции получения погоды и index.php для вывода прогноза с необходимыми нам параметрами. Выглядит это так:

Пример 1

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

Файл weather.php описывает функцию get_weather. В качестве обязательного параметра — только идентификатор города, который можно найти здесь:

http://weather.yandex.ru/static/cities.xml

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

В итоге получаем функцию get_weather со следующими параметрами:

  • $city — город
  • $col — количество дней, для которых получить погоду
  • $day_of_the_week_array — массив дней недели
  • $time_of_day — массив времени суток

Первый — обязательный, для других по умолчанию устанавливаем привычные «понедельник, вторник…» и «утро, день, вечер…»

Далее с помощью функции simplexml_load_file заполняем переменную $xml, в адрес подставляем идентификатор города, так Яндекс поймет, какой именно прогноз нам показать.

$data_file = 'http://export.yandex.ru/weather-ng/forecasts/'.$city.'.xml';   
$xml = simplexml_load_file($data_file);

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

Дату обрабатываем из xml следующим образом:

$get_date = explode ("-" , $day['date']) ;
$day_of_week = date("N", mktime(0, 0, 0, $get_date[1], $get_date[2], $get_date[0])) ;

$out[$counter]['day'] = $get_date[2] ;
$out[$counter]['month'] = $get_date[1] ;
$out[$counter]['year'] = $get_date[0] ;
$out[$counter]['day_of_week'] = $day_of_the_week_array[$day_of_week] ;

Разбиваем дату на составляющие (день, месяц, год), получаем затем день недели и заполняем все это в переменную вывода для текущего дня.

После этого открываем массив for от 0 до 3 — так мы получим данные температуры, изображение для погоды в разное время суток (файл Яндекса содержит больше информации, например, скорость ветра, если интересно, посмотрите больше и доработайте по аналогии).

Поясню получение температуры:

if($day->day_part[$i]->temperature == '') {

$get_temp_from =  $day->day_part[$i]->temperature_from;
$get_temp_to =  $day->day_part[$i]->temperature_to;

 }  else {

 $get_temp_from = (integer)$get_temp-1 ;
 $get_temp_to = (integer)$get_temp+1 ;

 }

Этот кусок кода выполняется после вхождения в массив, т.е. мы получаем информацию по температуре, к примеру, на утро. Здесь есть некоторая особенность. Яндекс может выдавать либо диапазон температур, в этом случае используются элементы temperature_from и temperature_to, либо статичное значение, но через temperature. Именно поэтому мы проверяем на пустоту значение temperature, если там данных не содержится, значит для выбранного времени суток используется диапазон, значение из которых мы присваиваем соответствующим переменным.

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

Добавляем также + для температуры больше нуля.

if($get_temp_from>0 ) {$get_temp_from = '+'.$get_temp_from ; }
 if($get_temp_to>0 ) {$get_temp_to = '+'.$get_temp_to ; }

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

$out[$counter]['weather'][$i]['temp_from'] = $get_temp_from;
 $out[$counter]['weather'][$i]['temp_to'] = $get_temp_to;
 $out[$counter]['weather'][$i]['image'] = $day->day_part[$i]->{'image-v3'};
 $out[$counter]['weather'][$i]['time_of_day'] = $time_of_day[$i] ;

Повторяем эту операцию для нужного количества дней и возвращаем массив $out.

В файле index.php создаем переменную, в которую в результате выполнения функции заносим этот самый массив, и теперь используем его для вывода информации. Используем при этом foreach для прохода по массиву.

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

Используя данный метод, можно создавать удобные сервисы, отображающие погоду в автоматическом режиме. В примере два мы выводим два выпадающих списка — страна и город. Данные подгружаем из файла cities.xml (ссылка на него выше). После выбора города погода подгружается через ajax автоматически.

Пример 2

Заметил для некоторых городов скрипт выдает ошибку — не находит прогноз. Это глюк Яндекса. Т.е город в списке есть, но при попытке получить xml для выбранного id оказывается, что файл не существует. Решение проблемы я вижу только в выводе текстового сообщение вроде «Прогноз погоды для выбранного города в данный момент не доступен».

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

ПОЛЕЗНО  Вывод случайной ссылки/картинки/чего либо если нада

Выход прост — пишем скрипт по закачке файлов к себе на сервер, ставим на cron и проделываем данную операцию раз в сутки (лучше ночью). Таким образом, получаем несколько тысяч xml-файлов прогнозов для каждого города и по ходу дня обращаемся уже к ним, а не к Яндексу. В этом случае также есть смысл ограничить количество городов парсинга. К примеру, если пишете скрипт для сайта туристического агенства — ограничьтесь городами, с которыми оно работает. Составьте массив необходимых городов и парсите только нужные.

Скачать исходники или с зеркала Прогноз погоды для сайта через API Яндекса

Источник

Красивая погода на сайт в стиле Метро.

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

Прогноз погоды для сайта через API Яндекса

Демо ι Скачать исходники или тут Красивая погода на сайт в стиле Метро.

Получение xml-файла погоды в цикле

Обновлено 05.04.2016

С недавних пор погода начала отдаваться с перебоями и пришлось прикрутить циклы с попытками получения файла. Вот так выглядит код:

<?php

// Заголовок с кодировкой
header('Content-Type: text/html; charset=UTF-8');

// Интересующий нас XML-файл
$xml_file = 'http://export.yandex.ru/weather-ng/forecasts/72483.xml';

// Количество попыток получить и обработать этот файл
$number_of_attempts = 0;

do
{
    // Выводим номер попытки
    echo 'Attempt = ', $number_of_attempts, '<br>';
    
    // Осуществляем попытку получить и обработать XML-файл
    $xml = simplexml_load_file($xml_file);
    
    usleep(100);
}
while (!$xml && ++$number_of_attempts <= 50); // Делаем пока не загрузили или пока количество попыток не будет превышать 50

// Итак, если XML-файл не обработан - пишем об этом
if (!$xml)
{
    exit('Could not load XML<br>');
}

// Иначе: Получаем температуру
$temp = $xml->fact->temperature;

// Форматируем её как нам нужно
if ($temp > 0) {
    $temp = '+' . $temp;
}

// И выводим
echo '<strong>Погода: ', $temp, ' °C</strong>';

Решение найдено было тут, но тоже не всегда срабатывает 🙁

И еще одно решение получения погоды и обработки ошибок

Предлагаю немного улучшить код сценария (на усмотрение автора, конечно).
Ключевые моменты:

  • обработка ошибок при запросе XML;
  • таймаут ожидания при запросе XML;
  • несколько попыток запроса XML;
  • проверка на существование нужных полей в XML;
  • запись в лог.
// Писать логи ( 0-нет, 1-только критические, 2-все )
$reclog = 1;

// Открываем лог, если нужно.
if ($reclog) { $log = getLogger('script.WeatherFromYandex'); }

// Адрес xml файла
$data_file = "http://export.yandex.ru/weather-ng/forecasts/$city_id.xml?".rand(1, 1000);

// Отключение ошибок libxml и передача полномочий по выборке и обработке информации об ошибках пользователю
libxml_use_internal_errors(true);

// Установим контекст потока
$ta = 30;   //Таймаут ожидания страницы от Яндекса (секунд)
$ctx = stream_context_create(array('http' => array('timeout' => $ta)));
libxml_set_streams_context($ctx);

$retries = 5;    // Число попыток запроса xml у Яндекса
$count = 0;        // Счетчик попыток
$xml = false;

while ( ($count !== $retries) && ($xml === false) ) {
    if ( $reclog == 2 ) $log->trace('Запрос данных погоды от сервиса Яндекс.Погода. Попытка = ' . $count);
     // Получаем xml от Яндекса и раскладываем на массив    
     $xml = @simplexml_load_file($data_file);
     if ( $reclog == 2 ) {
         if ( $xml === false ) $log->trace('Запрос данных погоды от сервиса Яндекс.Погода. Неуспешная попытка.');
           else $log->trace('Запрос данных погода от Яндекса. Успешная попытка.');
    }
     // Пауза между попытками
     sleep(1);
     $count++;
}

if($xml ===  false) { 
       if ($reclog) $log->error('Не удалось получить данные от сервиса Яндекс.Погода.');
    foreach(libxml_get_errors() as $error) {
         if ($reclog) $log->error($error->message);
    }
     libxml_clear_errors(); 
} 
else { 
    
     if( $fact = $xml->fact ) {
        
        // получаем нужные свойства, как раньше
         
    }
 
     if( $day0 = $xml->day[0] ) {
    
         // получаем нужные свойства, как раньше
        
    }
     
     if ( libxml_get_last_error() !== false ) {
    
         if ($reclog) $log->error('Данные от сервиса Яндекс.Погода получены, но возникли ошибки: ');
         
         foreach(libxml_get_errors() as $error) {
             if ($reclog) $log->error($error->message);
        }
    }
     
     libxml_clear_errors();
}

было найдено на этом форуме

ОБНОВЛЕНО 07.06.2016 — Yandex погода API

Нашлось вот такое решение:

<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <style type="text/css">
  body {
    font-family: Tahoma, sans-serif;
    font-size: 13px;
  }
 
  .pogoda_table {
    border: 1px solid #ccc;
    table-layout: fixed;
  }
 
  td {
    height: 28px;
    vertical-align: middle;
  }
 
  tr:nth-child(even) {
    background-color: #f2f2f2;
  }
 
  .td1 {
    padding-left: 5px;
    width: 40%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
 
  .td2 {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
 
  .td3 {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: center;
    text-decoration: underline;
    font-weight: bold;
  }
  </style>
</head>
<body>
<?php
$city = "28051";                                                       //  GeoId города (11310 - Минусинск)
$img_folder = "https://yastatic.net/weather/i/icons/islands/32/";      //  Папка с файлами изображений (https://yastatic.net/weather/i/icons/islands/32/)
$img_ext = "png";                                                      //  Расширение файлов картинок (svg / png)
 
//  Формируем запрос
$timestamp = time();
$token = md5('eternalsun'.$timestamp);
$uuid = "8211637137c4408898aceb1097921872";
$deviceid = "315f0e802b0b49eb8404ea8056abeaaf";
$opts = array(
  'http'=>array(
    'method'=>"GET",
    'header'=>"User-Agent: yandex-weather-android/4.2.1 \n" .
               "X-Yandex-Weather-Client: YandexWeatherAndroid/4.2.1 \n" .
               "X-Yandex-Weather-Device: os=null;os_version=21;manufacturer=chromium;model=App Runtime for Chrome Dev;device_id=$deviceid;uuid=$uuid; \n" .
               "X-Yandex-Weather-Token: $token \n" .
               "X-Yandex-Weather-Timestamp: $timestamp \n" .
               "X-Yandex-Weather-UUID: $uuid \n" .
               "X-Yandex-Weather-Device-ID: $deviceid \n" .
               "Accept-Encoding: gzip, deflate \n" .
               "Host: api.weather.yandex.ru \n" .
               "Connection: Keep-Alive \n"
  )
);
 
$context = stream_context_create($opts);
 
//  Отправляем запрос
$file = @file_get_contents('https://api.weather.yandex.ru/v1/forecast?geoid='. $city. '&lang=ru', false, $context);
 
if ($file){
//  Разархивируем ответ
$yandex = gzdecode($file);
 
$data_fact = (array) json_decode($yandex)->fact;    
$temp_current = $data_fact['temp'];                     //  Текущая температура
$icon_fact = $data_fact['icon'];                        //  Иконка текущей температуры
$icon_fact = preg_replace('/_/i','-',$icon_fact);       //  Заменяем символ "_" на символ "-"
$icon_fact = preg_replace('/--/i','-',$icon_fact);      //  Заменяем символы "--" на символ "-"
$wind_speed = $data_fact['wind_speed'];                 //  Скорость ветра
$humidity = $data_fact['humidity'];                     //  Влажность (%)
$pressure_mm = $data_fact['pressure_mm'];               //  Атмосферное давление (мм рт. ст.)
 
$data_forecasts0 = (array) json_decode($yandex)->forecasts[0];
$sunrise = $data_forecasts0['sunrise'];                 //  Рассвет
$sunset = $data_forecasts0['sunset'];                   //  Закат
 
//  Находим температуру на каждый час за сутки
for ($h = 0; $h<24; $h++) {
        $hour_array1[$h] = (int) json_decode($yandex)->forecasts[0]->hours[$h]->temp;
    }
$min_hour = min($hour_array1);                          //  Находим из них минимальную
$max_hour = max($hour_array1);                          //  И максимальную температуру
 
//  Находим температуру на каждый час за следующие сутки
for ($h = 0; $h<24; $h++) {
        $hour_array2[$h] = (int) json_decode($yandex)->forecasts[1]->hours[$h]->temp;
    }
$min_hour_t = min($hour_array2);                        //  Находим из них минимальную
$max_hour_t = max($hour_array2);                        //  И максимальную температуру
 
$data_forecasts2 = (array) json_decode($yandex)->forecasts[0]->parts->day;
$avg_day = $data_forecasts2['temp_avg'];                //  Находим среднюю температуру днем
$icon_day = $data_forecasts2['icon'];                   //  Иконка средней температуры днем
$icon_day = preg_replace('/_/i','-',$icon_day);         //  Заменяем символ "_" на символ "-"
$icon_day = preg_replace('/--/i','-',$icon_day);        //  Заменяем символы "--" на символ "-"
 
$data_forecasts3 = (array) json_decode($yandex)->forecasts[0]->parts->night;
$avg_night = $data_forecasts3['temp_avg'];              //  Находим среднюю температуру ночью
$icon_night = $data_forecasts3['icon'];                 //  Иконка средней температуры ночью
$icon_night = preg_replace('/_/i','-',$icon_night);     //  Заменяем символ "_" на символ "-"
$icon_night = preg_replace('/--/i','-',$icon_night);    //  Заменяем символы "--" на символ "-"
 
$data_forecasts4 = (array) json_decode($yandex)->forecasts[1]->parts->day;
$avg_day_t = $data_forecasts4['temp_avg'];              //  Находим среднюю температуру завтра днем
$icon_day_t = $data_forecasts4['icon'];                 //  Иконка средней температуры завтра днем
$icon_day_t = preg_replace('/_/i','-',$icon_day_t);     //  Заменяем символ "_" на символ "-"
$icon_day_t = preg_replace('/--/i','-',$icon_day_t);    //  Заменяем символы "--" на символ "-"
 
 
$data_forecasts5 = (array) json_decode($yandex)->forecasts[1]->parts->night;
$avg_night_t = $data_forecasts5['temp_avg'];            //  Находим среднюю температуру завтра ночью
$icon_night_t = $data_forecasts5['icon'];               //  Иконка средней температуры завтра ночью
$icon_night_t = preg_replace('/_/i','-',$icon_night_t); //  Заменяем символ "_" на символ "-"
$icon_night_t = preg_replace('/--/i','-',$icon_night_t);//  Заменяем символы "--" на символ "-"
 
//  Если значение температуры положительно, для наглядности добавляем "+"
if ($temp_current>0) {$temp_current='+'.$temp_current;}
if ($min_hour>0) {$min_hour='+'.$min_hour;}
if ($max_hour>0) {$max_hour='+'.$max_hour;}
if ($min_hour_t>0) {$min_hour_t='+'.$min_hour_t;}
if ($max_hour_t>0) {$max_hour_t='+'.$max_hour_t;}
if ($avg_day>0) {$avg_day='+'.$avg_day;}
if ($avg_night>0) {$avg_night='+'.$avg_night;}
if ($avg_day_t>0) {$avg_day_t='+'.$avg_day_t;}
if ($avg_night_t>0) {$avg_night_t='+'.$avg_night_t;}
}
?>
  <table class="pogoda_table" width="100%" cellpadding="0" cellspacing="0" style="background-color: #f8f8f8">
    <tr>
      <td class="td1" title="« Текущая температура »"><b>Сейчас:</b></td>
      <td class="td2" title="« Текущая температура »">
        <?php echo $temp_current; ?> <sup>o</sup>C
        <?php echo ("<img src=\"".$img_folder.$icon_fact ."." . $img_ext . "\" style=\"vertical-align: -7px;\" width=\"24px\" height=\"24px\"/>"); ?>
      </td>
    </tr>
    <tr>
      <td class="td1" title="« Скорость ветра »">Ветер:
        <?php echo $wind_speed; ?> м/с</td>
      <td class="td2" title="« Влажность воздуха »">Влажность:
        <?php echo $humidity; ?>%</td>
    </tr>
    <tr>
      <td class="td1" title="« Атмосферное давление »">Давление:</td>
      <td class="td2" title="« Атмосферное давление »">
        <?php echo $pressure_mm; ?> мм рт. ст.</td>
    </tr>
    <tr>
      <td class="td1" title="« Время восхода солнца »">Восход:
        <?php echo $sunrise; ?>
      </td>
      <td class="td2" title="« Время заката солнца »">Закат:
        <?php echo $sunset; ?>
      </td>
    </tr>
    <tr>
      <td class="td1" title="« Минимальная и максимальная температура сегодня »"><b>Сегодня:</b></td>
      <td class="td2" title="« Минимальная и максимальная температура сегодня »">от
        <?php echo $min_hour; ?> до
        <?php echo $max_hour; ?> <sup>o</sup>C</td>
    </tr>
    <tr>
      <td class="td1" title="« Средняя температура сегодня днем »">Днём:
        <?php echo $avg_day; ?>
        <?php echo ("<img src=\"".$img_folder. $icon_day ."." . $img_ext . "\" style=\"vertical-align: -7px;\" width=\"24px\" height=\"24px\"/>"); ?>
      </td>
      <td class="td2" title="« Средняя температура сегодня ночью »">Ночью:
        <?php echo $avg_night; ?>
        <?php echo ("<img src=\"".$img_folder.$icon_night ."." . $img_ext . "\" style=\"vertical-align: -7px;\" width=\"24px\" height=\"24px\"/>"); ?>
      </td>
    </tr>
    <tr>
      <td class="td1" title="« Минимальная и максимальная температура завтра »"><b>Завтра:</b></td>
      <td class="td2" title="« Минимальная и максимальная температура завтра »">от
        <?php echo $min_hour_t; ?> до
        <?php echo $max_hour_t; ?> <sup>o</sup>C</td>
    </tr>
    <tr>
      <td class="td1" title="« Средняя температура завтра днем »">Днём:
        <?php echo $avg_day_t; ?>
        <?php echo ("<img src=\"".$img_folder.$icon_day_t ."." . $img_ext . "\" style=\"vertical-align: -7px;\" width=\"24px\" height=\"24px\"/>"); ?>
      </td>
      <td class="td2" title="« Средняя температура завтра ночью »">Ночью:
        <?php echo $avg_night_t; ?>
        <?php echo ("<img src=\"".$img_folder.$icon_night_t ."." . $img_ext . "\" style=\"vertical-align: -7px;\" width=\"24px\" height=\"24px\"/>"); ?>
      </td>
    </tr>
    <tr>
      <td class="td3" title="« Перейти на сайт яндекса »" colspan="2">
        <a href="https://pogoda.yandex.ru/<?php echo $city ?>" target="_blank">Прогноз на неделю</a>
      </td>
    </tr>
  </table>
</body>
</html>

Результат выглядит вот так:

Прогноз погоды для сайта через API Яндекса

Прогноз погоды для сайта через API Яндекса

информация найдена тут в комментах

ПОЛЕЗНО  Логирование POST запросов к сайту

и список иконок погоды яндекса с расшифровками

bkn_-ra_d — облачно с прояснениями, небольшой дождь (день)
bkn_-ra_n — облачно с прояснениями, небольшой дождь (ночь)
bkn_-sn_d — облачно с прояснениями, небольшой снег (день)
bkn_-sn_n — облачно с прояснениями, небольшой снег (ночь)
bkn_d — переменная облачность (день)
bkn_n — переменная облачность (ночь)
bkn_ra_d — переменная облачность, дождь (день)
bkn_ra_n — переменная облачность, дождь (ночь)
bkn_sn_d — переменная облачность, снег (день)
bkn_sn_n — переменная облачность, снег (ночь)
bl — метель
fg_d — туман
ovc — облачно
ovc_-ra — облачно, временами дождь
ovc_-sn — облачно, временами снег
ovc_ra — облачно, дождь
ovc_sn — облачно, снег
ovc_ts_ra — облачно, дождь, гроза
skc_d — ясно (день)
skc_n — ясно (ночь)

«Яндекс» запустил API «Яндекс.Погоды» для владельцев сайтов

  • Как получить название города на английском как у них в ссылке инофрмера погоды?

  • yury

    ребят, ошибка здесь:

    if($day->day_part[$i]->temperature == '') {
    $get_temp_from =  $day->day_part[$i]->temperature_from;
    $get_temp_to =  $day->day_part[$i]->temperature_to;
     }  else {
     $get_temp_from = (integer)$get_temp-1 ;
     $get_temp_to = (integer)$get_temp+1 ;

    нужно так:

      if($day->day_part[$i]->temperature == '') {
       $get_temp_from =  $day->day_part[$i]->temperature_from;
       $get_temp_to =  $day->day_part[$i]->temperature_to;
        }  else {
        $get_temp_from = $day->day_part[$i]->temperature;
        $get_temp_to = $day->day_part[$i]->temperature;

    иначе, при отсутствии данных показывает -1…+1 из $get_temp_from = (integer)$get_temp-1 ;
    $get_temp_to = (integer)$get_temp+1 ;

  • olga

    Можно вариант 07.06.2016 приспособить, чтобы запрос шел с компьютера?

    • Roman NMSK

      да, на компе должен быть локальный веб-сервер и с него можно запускать, только предварительно нужно указать ID своего города 🙂

  • Владимир Иванченко

    добрый день, 2 день воюю не могу никак разобраться с запросом на перевод погодного описания, выдает на английском, может кто знает как сделать?