Как стать автором
Обновить

И снова Яндекс.Погода для сайта: время суток, направление ветра и прочие параметры

Время на прочтение6 мин
Количество просмотров58K
В продолжении поста о погоде «Яндекс.Погода для сайта в деталях». Прочитав данный пост, я пришел к выводу, что тема еще актуальна, и хотел бы дополнить выше упомянутую статью своими наработками.



Задача

Найти сервис, который отдавал бы данные погоды с прогнозом не менее 10 дней с русской локализацией, средними величинами, временами суток и дополнительными параметрами по заданному городу.

Поиски решения

Опробовав разные сервисы отдающие данные погоды, я остановился на Яндекс.Погода, все условия задачи совпадали.
Так как мне нужны были данные по одному городу, первое, что я сделал, это нашел идентификатор (id) города в списке, предоставленном здесь и подставил его в export.yandex.ru/weather-ng/forecasts/<id города>.xml. В итоге я получил xml-файл с данными по погоде выбранного мной города.

Разбор изображений

На втором этапе нужно было получить собственные изображения состояния погоды. В xml-файле этот параметр обозначен как image-v3. Чтобы получить весь список изображений, а так же описание для каждого состояния я набросал небольшой кустарный скрипт, который парсит города из доступного списка и собирает все в два массива на вывод.

<?php
// парсер изображений и описания состояний
$col = 1; // кол-во дней (до 10, увеличивается время выдачи)
$data_file = 'http://weather.yandex.ru/static/cities.xml';
$xml = simplexml_load_file($data_file);

foreach ($xml->country as $key => $value) {
	foreach ($value->city as $key1 => $value1):
		$out = getWeather($value1["id"], $col);
		foreach ($out as $day):
			foreach ($day['weather'] as $weather):
				$types[(string)$weather['weather_type']] = (string)$weather['image'];  // массив описаний состояния с изображением
				$images[(string)$weather['image']] = (string)$weather['weather_type']; // массив изображений с описанием состояния
			endforeach;
		endforeach;
	endforeach;
}
foreach($types as $type=>$img):
	echo '<img src="1450829064400515107291" width="48" height="48" /> -'. $type.'<br />';
endforeach;
echo '<br><hr><br>';
foreach($images as $img=>$type):
	echo '<img src="1450829064400515107291" width="48" height="48" /> -'. $type.'<br />';
endforeach;
/*
заполняем массив при помощи функции, первый параметр идентификатор города, другие параметры необязательны - в этом случае используется значения по умолчанию
*/
function getWeather($city, $col = 10) {
	$data_file = 'http://export.yandex.ru/weather-ng/forecasts/'.$city.'.xml';   // загружаем файл прогноза погоды для выбранного города
	$xml = simplexml_load_file($data_file); // загружаем xml файл через simple_xml

	$out = array(); // массив вывода прогноза
	$counter = 0 ; // счетчик количества дней, для которых доступен прогноз

	if($xml->day):
		foreach($xml->day as $day):

			if($counter == $col) break;
			for ($i=0;$i<=3;$i++) {
				$out[$counter]['weather'][$i]['image'] = $day->day_part[$i]->{'image-v3'};
				$out[$counter]['weather'][$i]['weather_type'] = $day->day_part[$i]->weather_type;
			}
			$counter++ ;
		endforeach;
	endif;
	return $out ;
}

На выходе получаем два массива данных, первый выводит весь список с описанием состояний и картинкой, второй выводит картинки с одним из видов описания состояния. Хочу заметить, что для одного изображения могут использоваться разные типы описаний состояния погоды. Параметр $col в скрипте, указывает на количество дней, который нужно обработать, максимум 10, по умолчанию 1, для уменьшения времени выдачи.

Примечание: скрипт работает довольно долго (особенно на локалхосте), т.к. городов много, поэтому желательно увеличить время выполнения php-скриптов (max_execution_time, max_input_time), а так же максимальный объем памяти скрипта (memory_limit).

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

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 — ясно (ночь)

Есть еще дополнительные иконки, но я не нашел где они используются, единственное отличие, это интенсивность осадков.

bkn_+ra_d, bkn_+ra_n, bkn_+sn_d, bkn_+sn_n, ovc_+ra, ovc_+sn

Реализация задачи

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

$city_id = 27612; // id Москвы
$url = 'http://export.yandex.ru/weather-ng/forecasts/'.$city_id.'.xml';
$userAgent = 'Googlebot/2.1 (+http://www.google.com/bot.html)';
$xml = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'weather_'.$city_id.'.xml';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
$output = curl_exec($ch);
$fh = fopen($xml, 'w');
fwrite($fh, $output);
fclose($fh);

Конечно, можно использовать кэширование в скрипте и сохранять в сериализованном виде (как в предыдущем посте), кому как удобней, но я остановился на своём велосипеде.

Далее мне осталось только загружать полученный xml-файл и стилизовать выдачу. Дабы не усложнять и не писать километровые классы, я скомпоную все в один файл.
$city_id = 27612; // id Москвы
$xml = $_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR.'weather_'.$city_id.'.xml';
if(file_exists($xml)):
	$data = simplexml_load_file($xml); //грузим ?>
	<style type="text/css">
	.weather{position:relative;border-bottom:1px solid #d5d5d5;padding-bottom:35px;}
	.weather .date{font-size:13px;font-weight:700;padding-bottom:5px;text-transform:uppercase;border-bottom:1px solid #d5d5d5;margin-top:10px;}
	.weather .item{background-color:#f0eedc;padding:15px;font-family:Georgia;margin-bottom:20px;}
	.weather .item table{border:0;width:100%;}
	.weather .item table td{padding-bottom:15px;width:20%;vertical-align:baseline;padding-right:5px;}
	.weather .item .day-part td{font-size:18px;}
	.weather .item .day-temp td{font-size:30px;}
	.weather .item .day-temp td img{margin-left:5px;}
	.weather .item .day-param td{font-size:12px;}
	.weather .item .day-param td p{padding-bottom:3px;}
	.weather .days{margin-top:35px;border:0;width:100%;}
	.weather .days td{width:50%;padding-bottom:35px;}
	.weather .days a{font-family:Georgia;font-size:18px;text-decoration:underline;font-weight:700;}
	</style>
	<div class="weather"><?php
	foreach($data->day as $day):?>
		<div class="date"><?php echo getDayDate($day['date']);?></div>
		<div class="item">
			<table>
				<tr class="day-part">
					<td>Утром</td>
					<td>Днем</td>
					<td>Вечером</td>
					<td>Ночью</td>
				</tr>
				<tr class="day-temp">
					<?php for($i = 0;$i < 4;$i++): // т.к. нам не нужны данные day_short и night_short, мы останавливаем проход на 4
					$img = $day->day_part[$i]->{'image-v3'};?>
					<td><?php echo getTempSign($day->day_part[$i]->{'temperature-data'}->avg);?> °C <img src="" width="48" height="48" /></td><?php endfor;?>
				</tr>
				<tr class="day-param">
					<?php for($i = 0;$i < 4;$i++): // т.к. нам не нужны данные day_short и night_short, мы останавливаем проход на 4?>
					<td>
						<p><strong><?php echo $day->day_part[$i]->weather_type;?></strong></p>
						<p>ветер: <?php echo getWindDirection($day->day_part[$i]->wind_direction).' '.$day->day_part[$i]->wind_speed;?> м/с</p>
						<p>влажность: <?php echo $day->day_part[$i]->humidity;?>%</p>
						<p>давление: <?php echo $day->day_part[$i]->pressure;?> мм рт. ст.</p>
					</td>
					<?php endfor;?>
				</tr>
			</table>
		</div><?php
	endforeach;?>
	</div><?php
endif;
// получаем локализованную дату
function getDayDate($date)
{
	$date = strtotime($date);
	$months = array('','января','февраля','марта','апреля','мая','июня','июля','августа','сентября','октября','ноября','декабря');
    $days = array('воскресенье','понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота');
	return $days[date('w', $date)].', '.(int)date('d',$date).' '.$months[date('n', $date)];
}
// получаем знак температуры
function getTempSign($temp)
{
	$temp = (int)$temp;
	return $temp > 0 ? '+'.$temp : $temp;
}
// получаем направления ветра
function getWindDirection($wind)
{
	$wind = (string)$wind;
	$wind_direction = array('s'=>'↑ ю','n'=>'↓ с','w'=>'→ з','e'=>'← в','sw'=>'↗ юз','se'=>'↖ юв','nw'=>'↘ сз','ne'=>'↙ св');
	return $wind_direction[$wind];
}

Скрипт выводит 10 дней по временам суток, направление ветра стрелками, как в Яндекс.Погода, так же берет усредненные значения температуры $day->day_part[$i]->{'temperature-data'}->avg, что дает нам одну цифру вместо двух.

Ссылки на исходники:
Парсер изображений и состояний
Выдача результата

Заключение

С поставленной задачей справились. В статье я хотел немного расширить представление информации сервисом Яндекс.Погода, расписывать все параметры не стал, их полный список можно посмотреть в xml-файле выдачи.
Теги:
Хабы:
+3
Комментарии19

Публикации

Истории

Работа

PHP программист
157 вакансий

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн