С развитием Вашего сайта рано или поздно Вам необходимо будет предоставлять дополнительную иноформацию, дополнительные сервисы пользователям, чтоб отбить их у своих конкурентов. Одним из них может стать погодный сервис. Использовать обычные погодные информеры для веб-программиста означало бы ухудшения качества своего сайта, так как обычные погодные информеры с трудом встраиваются в дизайн сайта, снабжены неуместными логотипами и тд. Поэтому иметь свой погодный информер, с собственными настройками, собственным дизайном представляется для меня лучшим вариантом.
Для достижения нашей цели нам понадобится:
База ip адресов состоит из нескольких блоков ip адресов. Первый блок - основной, содержится в файле cidr_ru_master_index.db. Второй блок ip адресов - дополнительный, он содержится в файле cidr_ru_slave_index.db в нём конкретезируется информация по ip адресам. Зачем два разных файла - поясню простым языком, первый содержит границы ip адресов для каждого региона, второй в рамках первого конкретизирует все ip адреса определённого региона. В первом содержаться ещё дополнительные данные, которые нам, для погодного информера не интересны.
Итак, во-первых, Вы скачали файл db_files.tar.gz, распакуйте его, в нем содержится ещё один файл - cidr_ru_block.txt, он дублирует оба файла, поэтому он нам не понадобится.
Зачем нам нужны файлы cidr_ru_master_index.db и cidr_ru_slave_index.db? С помощью этих фалов мы будем определять место расположения нашего пользователя, его адрес. Адрес будет определен центральным городом его субъекта. В Yandex и Rambler то же самое. Там пока не придумали как сделать погоду для точных городов пользователей?
Итак как мы будем использовать указанные файлы - очень просто, распарсим их в базу mysql, чтоб искалось быстрее. По крайней мере в моём случае искать по базе оказалось на много быстрее, чем по строчкам в файле.
Обе таблицы будут выглядеть следующим образом:
"SQL"
CREATE TABLE IF NOT EXISTS `master` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`ip1` bigint(20) unsigned NOT NULL,
`ip2` bigint(20) unsigned NOT NULL,
`country` varchar(2) NOT NULL,
`city` varchar(100) NOT NULL,
`region` varchar(100) NOT NULL,
`okrug` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
KEY `ip2` (`ip2`),
KEY `ip1` (`ip1`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
"SQL"
CREATE TABLE IF NOT EXISTS `geo` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`ip1` bigint(20) unsigned NOT NULL,
`ip2` bigint(20) unsigned NOT NULL,
`country` varchar(2) NOT NULL,
`city` varchar(100) NOT NULL,
`region` varchar(100) NOT NULL,
`okrug` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
KEY `ip2` (`ip2`),
KEY `ip1` (`ip1`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
Скрипт на php, который разбирает файлы следующий:
"PHP"
<?php
$master_file = 'cidr_ru_master_index.db';
$slave_file = 'cidr_ru_slave_index.db';
include '../config.php';
function my_ip_slave ($slave_file) {
$slave_fp = file($slave_file);
foreach ($slave_fp as $value) {
$cell = explode('\t', trim($value));
$connection = mysql_connect ("localhost", USER, PASS)
or die ("Ошибка соединения с сервером");
$db = mysql_select_db (DBS, $connection)
or die ("Ошибка при выборе базы данных");
mysql_query("SET NAMES 'utf8'");
mysql_query("SET CHARACTER SET 'utf8'");
set_time_limit(10000);
$query = "INSERT INTO geo (ip1, ip2, country, city,region,okrug) VALUES
('$cell[0]','$cell[1]','$cell[3]', '$cell[4]', '$cell[5]', '$cell[6]')";
$result = mysql_query ($query) or die ("Ошибка при выполнении запроса: ".mysql_error ());
}
}
function my_ip_master($master_file) {
$slave_fp = file($master_file);
foreach ($slave_fp as $value) {
$cell = explode('\t', trim($value));
$connection = mysql_connect ("localhost", USER, PASS)
or die ("Ошибка соединения с сервером");
$db = mysql_select_db (DBS, $connection)
or die ("Ошибка при выборе базы данных");
mysql_query("SET NAMES 'utf8'");
mysql_query("SET CHARACTER SET 'utf8'");
set_time_limit(10000);
$query = "INSERT INTO master (ip1, ip2, country, city,region,okrug) VALUES
('$cell[0]','$cell[1]','$cell[3]', '$cell[4]', '$cell[5]', '$cell[6]')";
$result = mysql_query ($query) or die ("Ошибка при выполнении запроса: ".mysql_error ());
}
}
my_ip_slave($slave_file);
my_ip_master($master_file);
?>
По поводу скрипта предупреждаю, на хостинге вызовет ошибку Fatal error: Allowed memory size..... Поэтому советую создать базу данных на локальной машине, установив параметр php_value memory_limit в php.ini побольше его стандартного значения, после чего дамп mysql импортировать на хостинг. Таблицы занимают около 15 mb.
Теперь вроде бы с ip мы разобрались, перейдём к сервису xml 1.2 от замечательного сайта http://weather.co.ua/services/ .
Сервис предоставляется бесплатно. Предоставляет он следующее - несколько файлов на php и несколько файлов xml. Нас интересуют следующие файлы - import_current.php, import_forecast.php, config.inc.php, common.inc.ph. Первые два - импортируют данные из xml в базу данных mysql, config.inc.php - настройки для подключения к базе данных, последний содержит функции для разбора xml.
Для импорта погодных данных Вам понадобится следующие таблицы sql:
"SQL"
DROP TABLE IF EXISTS `weather_current`;
DROP TABLE IF EXISTS `weather_forecast`;
CREATE TABLE `weather_current` (
`id` int(10) unsigned NOT NULL auto_increment,
`city_id` int(10) unsigned NOT NULL default 0,
`date` datetime NOT NULL,
`cloud` tinyint(3) unsigned NOT NULL default 0,
`t` tinyint(4) NOT NULL default 0,
`t_flik` tinyint(4) NOT NULL default 0,
`p` smallint(6) NOT NULL default 0,
`w` smallint(5) unsigned NOT NULL default 0,
`w_rumb` smallint(5) unsigned NOT NULL,
`h` tinyint(3) unsigned NOT NULL,
`last_updated` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `city_id` (`city_id`)
);
CREATE TABLE `weather_forecast` (
`id` int(10) unsigned NOT NULL auto_increment,
`city_id` int(10) unsigned NOT NULL,
`date` date NOT NULL,
`hour` tinyint(3) unsigned NOT NULL,
`cloud` tinyint(3) unsigned NOT NULL,
`precip` tinyint(3) unsigned NOT NULL,
`t_min` tinyint(4) NOT NULL,
`t_max` tinyint(4) NOT NULL,
`p_min` smallint(6) NOT NULL,
`p_max` smallint(6) NOT NULL,
`w_min` smallint(5) unsigned NOT NULL,
`w_max` smallint(5) unsigned NOT NULL,
`w_rumb` smallint(5) unsigned NOT NULL,
`h_min` tinyint(3) unsigned NOT NULL,
`h_max` tinyint(3) unsigned NOT NULL,
`wpi` tinyint(3) unsigned NOT NULL,
`last_updated` timestamp NOT NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `forecast_unique` (`city_id`,`date`,`hour`)
);
Самые для нас нужные - weather_current и weather_forecast. Эти таблицы я использую для создания погодного информера, две первые таблицы с сайта http://weather.co.ua вовсе не понадобились.
Для них важны два скрипта import_current.php и import_forecast.php.
Скрипт import_current.php нужен для отображения текущей погоды:
"PHP"
<?php
# include common lib and config file
include_once dirname(dirname(__file__))."/common.inc.php";
if (($fp = fopen(WEATHER_CURRENT_URL, "r")) !== false) {
$file_content = fread($fp, 16384);
# checking forecast version
if (preg_match('/fullcurrent version="1.2"/', $file_content)) {
while (!feof($fp)) {
$file_content .= fread($fp, 16384);
# get all city forecast
if (preg_match_all('@(.*?)@is', $file_content, $current_found)) {
foreach ($current_found[2] as $link_city_id => $inner_current_element) {
$current = readXmlElements($inner_current_element, true);
$current['city_id'] = $current_found[1][$link_city_id];
$sql = sprintf("INSERT INTO `%s` (`city_id`, `date`, `cloud`, `t`, `t_flik`, `p`, `w`, `w_rumb`, `h`)
VALUES (%d, '%s', %d, '%s', '%s', %d, %d, '%s', %d)
ON DUPLICATE KEY UPDATE
`date` = '%3\$s', `cloud` = '%4\$s', `t` = '%5\$s',
`t_flik` = '%6\$s', `p` = '%7\$s',
`w` = '%8\$s', `w_rumb` = '%9\$s',`h` = '%10\$d'",
WEATHER_CURRENT_TABLE_NAME,
$current['city_id'],
$current['date'],
$current['cloud'],
$current['t'],
$current['t_flik'],
$current['p'],
$current['w'],
$current['w_rumb'],
$current['h']
);
if (!mysql_ping()) {
mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS)
or DIE("Can't connect to a database server");
}
if (mysql_query($sql)) $added_count++;
else die(mysql_error());
$file_content = str_replace($current_found[0][$link_city_id], '', $file_content);
}
}
}
showMsg('Added '.$added_count.' current state of weather');
}
else showMsg('This is not 1.2 version of XML FullCurrent data');
}
else showMsg("Can't get XML FullCurrent data from '".WEATHER_FULL_FORECAST_URL."'");
?>
Скрипт import_forecast.php, нужен для отображения погоды на будущие 5 дней.
"PHP"
<?php
# include common lib and config file
include_once dirname(dirname(__file__))."/common.inc.php";
# download forecast data
if (($fp = fopen(WEATHER_FULL_FORECAST_URL, "r")) !== false) {
$file_content = fread($fp, 16384);
# checking forecast version
if (preg_match('/fullforecast version="1.2"/', $file_content)) {
while (!feof($fp)) {
$file_content .= fread($fp, 16384);
# get all city forecast
if (preg_match_all('@(.*?)@is', $file_content, $forecast_found)) {
foreach ($forecast_found[2] as $link_city_id => $inner_forecast_found) {
$added_count = $all_count = 0;
$city_id = $forecast_found[1][$link_city_id];
# get city forecast divided by date-hour
preg_match_all('@{4}-\d{2}-\d{2})" hour="(\d{1,2})">(.*?)@is', $inner_forecast_found, $hour_data);
foreach ($hour_data[3] as $key => $params) {
$forecast = readXmlElements($params, true);
$all_count++;
$sql = sprintf("INSERT INTO `%s` (`city_id`, `date`, `hour`, `cloud`,
`precip`, `t_min`, `t_max`, `p_min`,
`p_max`, `w_min`, `w_max`, `w_rumb`, `h_min`, `h_max`, `wpi`)
VALUES (%d, '%s', %d, %d, %d, '%s', '%s', %d, %d, %d, %d, '%d', %d, %d, %d)
ON DUPLICATE KEY UPDATE `cloud` = %5\$d, `precip` = %6\$d, `t_min` = '%7\$s',
`t_max` = '%8\$s', `p_min` = %9\$d, `p_max` = %10\$d, `w_min` = %11\$d,
`w_max` = %12\$d, `w_rumb` = '%13\$d', `h_min` = %14\$d, `h_max` = %15\$d,`wpi` = %16\$d",
WEATHER_FORECAST_TABLE_NAME,
$city_id,
$hour_data[1][$key],
$hour_data[2][$key],
$forecast['cloud'],
$forecast['ppcp'],
$forecast['t_min'],
$forecast['t_max'],
$forecast['p_min'],
$forecast['p_max'],
$forecast['wind_min'],
$forecast['wind_max'],
$forecast['wind_rumb'],
$forecast['h_min'],
$forecast['h_max'],
$forecast['wpi']
);
if (!mysql_ping()) {
mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS)
or DIE("Can't connect to a database server");
}
if (mysql_query($sql)) $added_count++;
else die(mysql_error());
}
showMsg('Added '.$added_count.' of '.$all_count.' forecast records for '.$city_id);
$file_content = str_replace($forecast_found[0][$link_city_id], '', $file_content);
}
}
}
}
else showMsg('This is not 1.2 version of XML FullForecast data ('.$parced_xml[0]['attrs']['VERSION'].')');
}
else showMsg("Can't get XML FullForecast data from '".WEATHER_FULL_FORECAST_URL."'");
?>
В обеих таблицах непонятным, скорее всего, будет запрос INSERT … ON DUPLICATE KEY UPDATE Он означает, что если не будет найдено ни одной записи, то будет добавлена новая, иначе будет обнавлена предыдущая запись.
Остальные функции можете посмотреть в файле common.inc.php.
Итак мы пришли к следующему - у Вас есть две таблицы с ip адресами - geo, master, две таблицы с данными о погоде - weather_forecast, weather_current. Осталось написать скрипт определяющий ip посетителя и выводящий соответсвующий прогноз для определенного города.
Приступим к нему, он будет называться base.php:
"PHP"
<?php
$offset = 15 * 21600;
header('Expires:'.gmdate('D,d M Y H:i:s', time() + $offset).'GMT');
header('Cache-control:public, max-age=3600');
header('Contetnt-type: text/xml; charset=UTF-8;');
include '../config.php';
if(isset($_POST['col'])) //Проверяем пришел ли GET запрос от jQuery
{
$title = $_POST['col'];
$start = ($_POST['col'] * 4)-4;
$end = 4;
}
else {$start = 0; $end = 4;}
class pogoda
{
function cloud($cloud,$hour)
{
if($cloud >= 0 && $cloud <10) {$pic = 0;}
if($cloud >= 10 && $cloud <20) {$pic = 10;}
if($cloud >= 20 && $cloud <30) {$pic = 20;}
if($cloud >= 30 && $cloud <40) {$pic = 30;}
if($cloud >= 40 && $cloud <50) {$pic = 40;}
if($cloud >= 50 && $cloud <60) {$pic = 50;}
if($cloud >= 50 && $cloud <70) {$pic = 60;}
if($cloud >= 70 && $cloud <80) {$pic = 70;}
if($cloud >= 80 && $cloud <90) {$pic = 80;}
if($cloud >= 90 && $cloud <100) {$pic = 90;}
if($cloud == 255) {$pic = 90;}
if($pic == 0 && $hour == 3){$pic = "01";}
if($pic == 10 && $hour == 3){$pic = "02";}
switch ($pic)
{
case "01"; echo "
"; break;
case "02"; echo "
"; break;
case "0"; echo "
"; break;
case "10"; echo "
"; break;
case "20"; echo "
"; break;
case "30"; echo "
"; break;
case "40"; echo "
"; break;
case "50"; echo "
"; break;
case "60"; echo "
"; break;
case "70"; echo "
"; break;
case "80"; echo "
"; break;
case "90"; echo "
"; break;
case "100"; echo "
"; break;
case "255"; echo "
"; break;
}
}
function day($day)
{
$data = explode("-",$day);
$den = date("D", mktime(0, 0, 0, $data[1], $data[2],$data[0]));
switch($den)
{
case "Mon"; echo "Пн"; break;
case "Tue"; echo "Вт"; break;
case "Wed"; echo "Ср"; break;
case "Thu"; echo "Чт"; break;
case "Fri"; echo "Пт"; break;
case "Sat"; echo "Сб"; break;
case "Sun"; echo "Вс"; break;
}
}
function datime($datetime)
{
if($datetime == 3){echo 'ночь';}
if($datetime == 9){echo 'утро';}
if($datetime == 15){echo 'день';}
if($datetime == 21){echo 'вечер';}
}
}
include 'pogoda.php';
$conn = mysql_connect('localhost', USER,PASS)
or die('Error'.mysql_error());
$db = mysql_select_db(DBS,$conn);
mysql_query("SET NAMES 'utf8'");
mysql_query("SET CHARACTER SET 'utf8'");
$csql='SELECT * FROM weather_current WHERE city_id='.$url;
$cquery = mysql_query($csql);
$toptab = '<table width="100%" style="height: 160px;" align="center"><tr><td colspan="4" align="center">'.$city1.'';
while($res=mysql_fetch_assoc($cquery))
{
$toptab .= $bezb = '+'.$res['t']. ' ' .$res['p'];
}
$where = array(sprintf('w.city_id = %d', $url));
$where[] = sprintf('((w.date = CURDATE() AND w.hour > %d) OR w.date > CURDATE())', date('G'));
$order = " ORDER BY ID ASC LIMIT $start,$end";
$_sql = '
SELECT UNIX_TIMESTAMP(date) date_text,
DATE_FORMAT(date, \'%%w\') day, UNIX_TIMESTAMP(date) date_unix, DATE_SUB(w.date, INTERVAL 1 DAY) date_night,
date, hour, cloud, precip, t_min, t_max, p_min, p_max, w_min, w_max, w_rumb, h_min, h_max, wpi AS accuracy
FROM %s w
WHERE %s %s
';
$sql = sprintf($_sql,
"weather_forecast",
implode(' AND ', $where),
$order
);
$query = mysql_query($sql);
$result=mysql_num_rows($query);
if ($result >= 1)
{
echo $toptab;
while($res=mysql_fetch_assoc($query))
{
$func = new pogoda;
echo '<tr><td align="center">';
$func -> day($res['date']);
echo '<td align="center">';
$func -> datime($res['hour']);
echo '<td align="center">';
echo '+'.$res['t_min'].'+'.$res['t_max'].'</br>';
echo '<td align="center">';
$func -> cloud($res['cloud'],$res['hour']);
}
}
else
{
echo '<table width="100%" style="height: 100%;" align="center"><tr><td colspan="4" align="center">'.$city1.'';
echo $bezb;
echo '<tr><td align="center">Данные обновляются</td>';
}
echo '</table>';
mysql_close($conn);?>
В скрипте достаточно комментариев, скрипт выводит данные из таблиц с использованием другого скрипта pogoda.php. Его содержание следующее:
"PHP"
<?php
$connection = mysql_connect('localhost', USER,PASS)
or die ("Ошибка соединения с сервером");
$db = mysql_select_db (DBS, $connection)
or die ("Ошибка при выборе базы данных");
mysql_query("SET NAMES 'utf8'");
mysql_query("SET CHARACTER SET 'utf8'");
$ip = $_SERVER['REMOTE_ADDR'];
$ra1 = sprintf("%u", ip2long($ip));
if ($ra1=='') return '';
$ra = mysql_query("SELECT city, (ip2-ip1) as dst
FROM geo WHERE ('".$ra1."')>=ip1 and ('".$ra1."')<=ip2 ORDER BY dst ASC LIMIT 1",$connection);
if (mysql_num_rows($ra)>0)
{
$cn = mysql_fetch_array($ra);
$city1 = trim($cn['city']);
include "city.php";
}
else
{
$ra = mysql_query("SELECT city, (ip2-ip1) as dst
FROM master WHERE ('".$ra1."')>=ip1 and ('".$ra1."')<=ip2 ORDER BY dst ASC LIMIT 1",$connection);
if (mysql_num_rows($ra)>0)
{
$cn = mysql_fetch_array($ra);
$city1 = trim($cn['city']);
include "city.php";
}
}
mysql_close($connection);
?>
Так же как и прежний скрипт он не предоставляет сложности, для его понимания нужен лишь скрипт city.php. Весь скрипт я приводить не буду, укажу лишь образец первых строк, весь скрипт вы можете скачать внизу страницы со всем архивом.
"PHP"
<?php
if ($city1=="Абакан")
{
$url = "1407";
}
elseif ($city1=="Анадырь")
{
$url = "1568";
}
elseif ($city1=="Архангельск")
{
$url = "342";
}
Таким образом с помощью ip мы определим центр краевой,областной для пользователя вашего сайта. И передадим его скрипту base.php, который в свою очередь выведет соответсвующую информацию (SELECT * FROM weather_current WHERE city_id='.$url).
Конечно же осталось только сделать информер, его дизайн и придать хорошую функциональность на jquery, чтоб пользователь мог пролистывать всю погоду не переходя по страницам, а использовал бы jQuery.
"jQuery"
jQuery(document).ready(function(){
$("#pogoda").bind("ajaxSend",function(){
$(this).fadeOut();}).bind("ajaxComplete",function(){
$(this).fadeIn();});
$("#load").bind("ajaxSend",function(){$(this).fadeIn();}).bind("ajaxComplete",function(){
$(this).fadeOut();});
$("#pogoda").fadeIn('slow');
jQuery('#button').click(function(){
var title=$("#button").attr("title");
jQuery.post('../slyweather/base.php',{col:title},function(xml){jQuery('#pogoda').html('');
jQuery('#pogoda').html(xml).fadeIn("slow");},'html');
if(title==2){
$("#button1").attr({title:"1"});
$("#button").attr({title:"3"});$(".next").css({'display':'block'});
$(".back").css({'display':'block'});
}
if(title==3){
$("#button1").attr({title:"2"});
$("#button").attr({title:"4"});
$(".next").css({'display':'block'});
$(".back").css({'display':'block'});
}
if(title==4){
$("#button1").attr({title:"3"});
$("#button").attr({title:"5"});
$(".next").css({'display':'block'});
$(".back").css({'display':'block'});
}
if(title==5){
$("#button1").attr({title:"4"});
$("#button").attr({title:""});
$(".next").css({'display':'none'});
$(".back").css({'display':'block'});}});
jQuery('#button1').click(function(){
var title=$("#button1").attr("title");
jQuery.post('../slyweather/base.php',{col:title},function(xml){
jQuery('#pogoda').html('');jQuery('#pogoda').html(xml).fadeIn("slow");},'html');
if(title==1){$("#button").attr({title:"2"});$(".back").css({'display':'none'});}
if(title==2){$("#button").attr({title:"3"});$("#button1").attr({title:"1"});}
if(title==3){$("#button").attr({title:"4"});$("#button1").attr({title:"2"});}
if(title==4){$("#button").attr({title:"5"});$("#button1").attr({title:"3"});$(".next").css({'display':'block'});}
});
});
"HTML"
<div style="width:100%; height:100%;background-image: url(1.png);padding-top:30px;">
<div style="width:200px;margin-left:50px;">
<div class="navbar" style="opacity:0.9;" id="voteclone">
<span class="corners-top"><span></span></span>
<!-- ////// ПОГОДА ////// -->
<table>
<tr>
<td><div title="1" class = "back" id = "button1"></div>
<td><div title="2" class = "next" id = "button"></div>
<td><div id="load"></div>
</table>
<table width="100%">
<tr><td align="center"><a href="http://weather.co.ua/"><img src="http://weather.co.ua/images/weatherXML_bl.gif" border="0" width="99" class="pic2" height="17" alt="" title="Погода от weather.co.ua"></a>
</table>
<span class="corners-bottom"><span></span></span>
</div>
<div>
<div>
14.1
Александр
У меня такое было когда кодировка базы и кодировка таблицы были разные. Эта кодировкаmysql_query("SET NAMES 'utf8'");должна соответствовать кодировке mysql. Попробуйте всё в UTF8.