Digs - Персональная территория

Авторский проект Артема Глазкова
? 
        Версия для печати (цвет)  

Использование материалов
Заметка #6
19 мая 2005

Пишем счетчик


    Зачем и что
    Для начала расскажу, зачем я все это затеял.
    Сервер Apache создает файл access.log в который попадает информация обо всех посещениях сайта: кто заходил, когда, какие страницы посетил. Политика моего хостера (на момент написания статьи) такова, что этот лог обнуляется в начале каждого часа. Естественно, это не дает возможности, какого либо анализа. Вторая причина, хочется разобраться с действиями роботов, что именно они взяли с сайта, периоды индексации и т.д. Причем не анализировать каждый раз лог, а всегда иметь перед глазами краткую информацию. Ну и последняя причина, это найти замену стандартным счетчикам, которые предоставляют такие сервера, как SpyLog. Это позволит увеличить скорость работы сайта (для меня это важно).

    Реализация
    Всю информацию будем собирать в одном файле. Файл текстовый. Каждая строка это: время, IP посетителя (либо имя робота) и адрес станицы. Для того чтобы лог было проще анализировать, время будем записывать в формате UNIX timestamp. Новая строка всегда будет вставляться в начало лога. Все записи старше месяца будем удалять.
    Для начала определим, зашел к нам робот или обычный пользователь. В отличие от скрипта, описанного в статье “Защита статей с помощью клоакинга”, нам также нужно получить имя робота. Также в списке роботов присутствуют короткие однобуквенные коды для некоторых роботов, которые затем будут отображаться в счетчике. Эти буквы записаны через пробел после основного имени и нам придется их выделить, чтобы правильно проверить имя.
    Так как есть роботы, которые имеют в своем имени символы пробела, поэтому в целях, не противоречащих предыдущему предложению, я заменил “ ” на символ “#”. С учетом всего сказанного получаем следующий скрипт:

$isrobot false;
$robotname "";
if (isset(
$_SERVER["HTTP_USER_AGENT"])) {
  
$s strtolower($_SERVER["HTTP_USER_AGENT"]);
  
$robots = array();
  
$r file("robots.list");  
  
//делаем разбивку по пробелу: у имени индекс в массиве – 0
  //у кода - 1
  
foreach($r as $line$robots[] = explode(" ",$line);
  foreach(
$robots as $item){
    
$r str_replace("\n","",$item[0]);
    
$r str_replace("\r","",$r);
    
//заменяем #, чтобы получить реальную подстроку имени робота
    
$r1 str_replace("#"," ",$r);
    if (
strpos($s,strtolower($r1))!==false){
      
//если подстрока нашлась, считаем что это робот и запоминаем имя
      
$robotname $r;
      
$isrobot true;
      break;
    }
  }
}

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

function AddInfoToLog($specaddr=""){    
  global 
$isrobot,$robotname,$log;

  
//Если робот, то подставляем его имя, вместо IP
  
if ($isrobot$ip $robotname; else $ip $_SERVER["REMOTE_ADDR"];    
  
//здесь делаем строку
  
if ($specaddr!=""$s .= time().$ip ".$specaddr;
  else{
    
$s time().$ip ".$_SERVER["SCRIPT_NAME"];
    if (isset(
$_SERVER["QUERY_STRING"])) $s .= "?".$_SERVER["QUERY_STRING"];
  }
  
$s .= "\n";
  
//получает текущие дату/время и вычитаем из этого 30 дней –
  //максимальное время хранения записи в логе
  
$a getdate(time());
  
$limtime mktime($a['hours'],$a['minutes'],$a['seconds'],$a['mon'],
                    
$a['mday'] - 30,$a['year']);
  
//открываем файл и блокируем его  
  
$handle fopen("counter.dat","r+");
  @
flock($handle,LOCK_EX);
  
//добавляем в массив новую строку
  
$log = array($s);
  
//считываем файл построчно и добавляем в массив
  
while (!feof ($handle)) {
    
$line fgets($handle);
    
$items explode(" ",$line); 
    
//если дата записи слишком стара, прекращаем читать файл.
    
if (isset($items[0]) && $items[0]<$limtime) break;
    
$log[] = $line;
  }
  
//обнуляем файл
  
ftruncate($handle,0);
  
//переводим указатель файла в начало, иначе при первой же записи
  //файл будет заполнен нулями до текущей позиции
  
fseek($handle,0);
  
//сохраняем массив, разблокируем файл и закрываем.
  
foreach($lines as $linefwrite($handle,$line);
  
flock($handle,LOCK_UN);
  
fclose($handle);
}


    Вот лог и сформировали. Теперь нужно отобразить счетчик. Так как при добавлении записи мы поместили весь лог в переменную $log, нам нет надобности считывать его вновь из файла.
    Данные с счетчике сверху вниз отображают данные: количество запрошенных страниц за неделю, количество хостов за неделю, количество запрошенных страниц за последние сутки и количество хостов за сутки.
    Вычислим две временные точки: сутки назад и неделю назад.

$a getdate(time());
$date1 mktime($a['hours'],$a['minutes'],$a['seconds'],
                
$a['mon'],$a['mday'] - 1,$a['year']);
$date2 mktime($a['hours'],$a['minutes'],$a['seconds'],
                
$a['mon'],$a['mday'] - 7,$a['year']);

    Теперь заводим массив $ips, индексами которого будут IP-адреса либо имена роботов. Элементами – массивы из двух ячеек. Первая это количество посещений с данного хоста за сутки и второй – за неделю.

$ips = array();
foreach(
$log as $item){
  
$words explode(" ",$item);
  if (isset(
$words[0]) && $words[0]<$date2) break;
  if (!isset(
$words[1]) || !isset($words[2])) continue;
  
$ip $words[1];
  if (isset(
$ips[$ip])) $ips[$ip][1]++; else $ips[$ip] = array(0,1);
  if (
$words[0]>$date1$ips[$ip][0]++;
}

   Далее пробегаем массив и делаем простой подсчет.

$text1 0//всего за неделю
$text2 0//уникальных за день
$text3 0//всего за день
foreach($ips as $item){
  
$text1 += $item[1];
  if (
$item[0]!=0$text2++;
  
$text3 += $item[0];
}

    Подсчитали – выводим. Количество уникальных хостов за неделю – это count($ips).


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

if (isset($_SESSION["count"])) $_SESSION["count"]++; 
  else 
$_SESSION["count"] = 1;

    Кусок добавления значения переменной $_SESSION[“count”] приводить не буду. Остановимся только на участке вычисления количества хостов и посещений:

$ips = array();
foreach(
$this->log as $item){
  
//удаляем символы перевода строки
  
$item str_replace(array("\n","\r"),"",$item);
  
//извлекаем все части строки в массив
  
$words explode(" ",$item);
  
//записи более чем недельной давности нас не интересуют
  
if (isset($words[0]) && $words[0]<$date2) break;
  
//проверяем, если есть четвертый параметр и равен 1,
  //то это новое посещение
  
if (isset($words[3]) && $words[3]=="1"){
    
$ip $words[1];
    
//если новый хост, добавляем в массив хостов
    
if (!isset($ips[$ip])) $ips[$ip] = array(0,0);
    
//увеличиваем посещение за неделю
    
$ips[$ip][1]++;  
    
//если прошло меньше суток, то увеличиваем за сутки
    
if ($words[0]>$date1$ips[$ip][0]++; 
   }       
}

    Как видно из кода, происходит проверка наличия четвертого параметра. Это делает совместимость со старыми строками лога, а также означает, что счетчик обрабатывает только новые посещения. Т.е. он как бы стартует с начала.
    Код нахождения сумм посещений и хостов не привожу – остался прежним.
    Еще можно посоветовать анализировать второй параметр на имя робота. Здесь просто, если первый символ не цифра, то робот. В этом случае не создавать элемент массива $ips. Тогда циферки будут отображать посещения только людей.


© 2005-16, Powered By Digs (Написать письмо, vk)