Как составить статистику на сайт

Как сделать статистику на сайт

С момента появления первых сайтов прошло немало времени. С тех пор количество пользователей интернета увеличилось в миллионы раз. Поэтому проблема сбора точной, полной и достоверной статистики о посетителях стала весьма актуальной для большинства web-мастеров. К счастью, в настоящее время доступны несколько мощных бесплатных сервисов сбора и обработки информации о трафике web-ресурсов, поэтому уже не приходится задаваться вопросом о том, как сделать статистику на сайт. Один из подобных сервисов – Google Analytics.

Как сделать статистику на сайт

Вам понадобится

  • Любой современный web-браузер. Возможность редактирования шаблонов страниц web-сайта.

Инструкция

Зарегистрируйтесь в Google Analytics. Для этого откройте в браузере адрес http://www.google.com/analytics/, кликните по ссылке “Sign Up Now”, кликните по ссылке “Create an account now”. Откроется страница регистрации нового аккаунта. Пройдите процедуру регистрации. Для удобства выберите предпочитаемый язык интерфейса в выпадающем списке, расположенном сверху справа на странице.

Как сделать <b>статистику</b> на сайт

Войдите в аккаунт Google Analytics. Откройте в браузере адрес http://www.google.com/analytics/, кликните по кнопке “Access Analytics”. В выпадающем списке “Изменить язык:” выберите значение “Русский”. В поля “Электронная почта” и “Пароль” введите свои учетные данные от аккаунта. Нажмите кнопку “Войти”.

Как сделать <b>статистику</b> на сайт

Добавьте профиль web-сайта в аккаунт Google Analytics. На странице https://www.google.com/analytics/settings/home кликните по ссылке “Добавить профиль веб-сайта”. На открывшейся странице введите домен сайта. Нажмите кнопку “Готово”.

Как сделать <b>статистику</b> на сайт

Установите код отслеживания на сайте. На странице, открывшейся после добавления профиля, содержится фрагмент JavaScript-кода и инструкции по его установке на сайт. Код необходимо разместить на всех страницах сайта для сбора статистики. Ознакомьтесь с инструкциями по установке кода, приведенными на странице. Скопируйте код. Отредактируйте шаблоны страниц сайта таким образом, чтобы код располагался в конце элемента HEAD страниц. Внизу страницы Google Analytics нажмите кнопку “Сохранить и завершить”.

Как сделать <b>статистику</b> на сайт

Дождитесь подтверждения начала сбора статистики. В колонке “Статус” списка профилей web-сайтов появится значок в виде зеленой галочки. Реальная статистика посещений может начать отображаться в течение нескольких часов.

Как сделать <b>статистику</b> на сайт

Обратите внимание

Если сайт структурно расположен на нескольких поддоменах (например, основной сайт, блог, форум), то необходимо добавить в систему каждый поддомен. Поскольку фактически это разные сайты с разными именами хостов.

Полезный совет

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

Источники:

  • Описание продукта Google Analytics в 2019

Войти на сайт

или

Забыли пароль?
Еще не зарегистрированы?

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Статистика сайта и своё маленькое хранилище

Время на прочтение
9 мин

Количество просмотров 5.9K

Утилита Webalizer и инструмент Google Analytics помогали мне много лет получать представление о том, что происходит на веб сайтах. Сейчас я понимаю, что они дают очень мало полезной информации. Имея доступ к своему файлу access.log, разобраться со статистикой очень просто и для реализации достаточно элементарных инструментов, таких как sqlite, html, языка sql и любого скриптового языка программирования.

Источником данных для Webalizer является файл access.log сервера. Так выглядят его столбики и цифры, из которых понятен лишь общий объём трафика:

image

image

Такие инструменты, как Google Analytics собирают данные с загруженной страницы самостоятельно. Отображают нам пару диаграмм и линий, на основе которых часто сложно сделать правильные выводы. Может быть, нужно было приложить больше усилий? Не знаю.

Итак, что мне хотелось увидеть в статистике посещений сайта?

Трафик пользователей и ботов

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

image

SQL запрос отчёта

SELECT
1 as 'StackedArea: Traffic generated by Users and Bots',
strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Day',
SUM(CASE WHEN USG.AGENT_BOT!='n.a.' THEN FCT.BYTES ELSE 0 END)/1000 AS 'Bots, KB',
SUM(CASE WHEN USG.AGENT_BOT='n.a.' THEN FCT.BYTES ELSE 0 END)/1000 AS 'Users, KB'
FROM
  FCT_ACCESS_USER_AGENT_DD FCT,
  DIM_USER_AGENT USG
WHERE FCT.DIM_USER_AGENT_ID=USG.DIM_USER_AGENT_ID
  AND datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DT

Из графика видна постоянная активность ботов. Интересно было бы детально изучить наиболее активных представителей.

Назойливые боты

Классифицируем ботов на основе информации пользовательского агента. Дополнительная статистика о дневном трафике, количестве успешных и безуспешных запросов даёт хорошее представление об активности ботов.

image

SQL запрос отчёта

SELECT 
1 AS 'Table: Annoying Bots',
MAX(USG.AGENT_BOT) AS 'Bot',
ROUND(SUM(FCT.BYTES)/1000 / 14.0, 1) AS 'KB per Day',
ROUND(SUM(FCT.IP_CNT) / 14.0, 1) AS 'IPs per Day',
ROUND(SUM(CASE WHEN STS.STATUS_GROUP IN ('Client Error', 'Server Error') THEN FCT.REQUEST_CNT / 14.0 ELSE 0 END), 1) AS 'Error Requests per Day',
ROUND(SUM(CASE WHEN STS.STATUS_GROUP IN ('Successful', 'Redirection') THEN FCT.REQUEST_CNT / 14.0 ELSE 0 END), 1) AS 'Success Requests per Day',
USG.USER_AGENT_NK AS 'Agent'
FROM FCT_ACCESS_USER_AGENT_DD FCT,
     DIM_USER_AGENT USG,
     DIM_HTTP_STATUS STS
WHERE FCT.DIM_USER_AGENT_ID = USG.DIM_USER_AGENT_ID
  AND FCT.DIM_HTTP_STATUS_ID = STS.DIM_HTTP_STATUS_ID
  AND USG.AGENT_BOT != 'n.a.'
  AND datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY USG.USER_AGENT_NK
ORDER BY 3 DESC
LIMIT 10

В данном случае результатом анализа стало решение об ограничении доступа к сайту путём добавления в файл robots.txt

User-agent: AhrefsBot
Disallow: /
User-agent: dotbot
Disallow: /
User-agent: bingbot
Crawl-delay: 5

Первые два бота исчезли из таблицы, а роботы MS подвинулись с первых строчек вниз.

День и время наибольшей активности

В трафике видны подъёмы. Чтобы детально их исследовать, необходимо выделить время их возникновения, при этом не обязательно отображать все часы и дни измерения времени. Так будет проще найти отдельные запросы в лог файле при необходимости детального анализа.

image

SQL запрос отчёта

SELECT
1 AS 'Line: Day and Hour of Hits from Users and Bots',
strftime('%d.%m-%H', datetime(EVENT_DT, 'unixepoch')) AS 'Date Time',
HIB AS 'Bots, Hits',
HIU AS 'Users, Hits'
FROM (
	SELECT
	EVENT_DT,
	SUM(CASE WHEN AGENT_BOT!='n.a.' THEN LINE_CNT ELSE 0 END) AS HIB,
	SUM(CASE WHEN AGENT_BOT='n.a.' THEN LINE_CNT ELSE 0 END) AS HIU
	FROM FCT_ACCESS_REQUEST_REF_HH
	WHERE datetime(EVENT_DT, 'unixepoch') >= date('now', '-14 day')
	GROUP BY EVENT_DT
	ORDER BY SUM(LINE_CNT) DESC
	LIMIT 10
) ORDER BY EVENT_DT

Наблюдаем самые активные часы 11, 14 и 20 первого дня на графике. А вот на следующий день в 13 часов активничали боты.

Средняя дневная активность пользователей по неделям

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

image

SQL запрос отчёта

SELECT
1 as 'Line: Average Daily User Activity by Week',
strftime('%W week', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Week',
ROUND(1.0*SUM(FCT.PAGE_CNT)/SUM(FCT.IP_CNT),1) AS 'Pages per IP per Day',
ROUND(1.0*SUM(FCT.FILE_CNT)/SUM(FCT.IP_CNT),1) AS 'Files per IP per Day'
FROM
  FCT_ACCESS_USER_AGENT_DD FCT,
  DIM_USER_AGENT USG,
  DIM_HTTP_STATUS HST
WHERE FCT.DIM_USER_AGENT_ID=USG.DIM_USER_AGENT_ID
  AND FCT.DIM_HTTP_STATUS_ID = HST.DIM_HTTP_STATUS_ID
  AND USG.AGENT_BOT='n.a.' /* users only */
  AND HST.STATUS_GROUP IN ('Successful') /* good pages */
  AND datetime(FCT.EVENT_DT, 'unixepoch') > date('now', '-3 month')
GROUP BY strftime('%W week', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DT

Статистика за неделю показывает, что в среднем один пользователь открывает 1,6 страниц в день. Количество запрашиваемых файлов на одного пользователя в данном случае зависит от добавления новых файлов на сайт.

Все запросы и их статусы

Webalizer всегда показывал конкретные коды страниц и хотелось всегда видеть просто количество успешных запросов и ошибок.

image

SQL запрос отчёта

SELECT
1 as 'Line: All Requests by Status',
strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Day',
SUM(CASE WHEN STS.STATUS_GROUP='Successful' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Success',
SUM(CASE WHEN STS.STATUS_GROUP='Redirection' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Redirect',
SUM(CASE WHEN STS.STATUS_GROUP='Client Error' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Customer Error',
SUM(CASE WHEN STS.STATUS_GROUP='Server Error' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Server Error'
FROM
  FCT_ACCESS_USER_AGENT_DD FCT,
  DIM_HTTP_STATUS STS
WHERE FCT.DIM_HTTP_STATUS_ID=STS.DIM_HTTP_STATUS_ID
  AND datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DT

Отчёт отображает запросы, а не клики (хиты), в отличие от LINE_CNT метрика REQUEST_CNT считается как COUNT(DISTINCT STG.REQUEST_NK). Цель — показать эффективные события, например, боты MS сотни раз в день опрашивают файл robots.txt и, в данном случае, такие опросы будут посчитаны один раз. Это позволяет сгладить скачки на графике.

Из графика можно увидеть много ошибок — это несуществующие страницы. Результатом анализа стало добавление перенаправлений с удалённых страниц.

Ошибочные запросы

Для детального рассмотрения запросов можно вывести детальную статистику.

image

SQL запрос отчёта

SELECT
  1 AS 'Table: Top Error Requests',
  REQ.REQUEST_NK AS 'Request',
  'Error' AS 'Request Status',
  ROUND(SUM(FCT.LINE_CNT) / 14.0, 1) AS 'Hits per Day',
  ROUND(SUM(FCT.IP_CNT) / 14.0, 1) AS 'IPs per Day',
  ROUND(SUM(FCT.BYTES)/1000 / 14.0, 1) AS 'KB per Day'
FROM
  FCT_ACCESS_REQUEST_REF_HH FCT,
  DIM_REQUEST_V_ACT REQ
WHERE FCT.DIM_REQUEST_ID = REQ.DIM_REQUEST_ID
  AND FCT.STATUS_GROUP IN ('Client Error', 'Server Error')
  AND datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-14 day')
GROUP BY REQ.REQUEST_NK
ORDER BY 4 DESC
LIMIT 20

В этом списке будут находиться и все прозвоны, например, запрос к /wp-login.php Путём корректировки правил переписывания запросов сервером можно скорректировать реакцию сервера на подобные запросы и отправлять их на стартовую страницу.

Итак, несколько простых отчётов на основе файла лога сервера дают достаточно полную картину того, что происходит на сайте.

Как получить информацию?

Базы данных sqlite вполне достаточно. Создадим таблицы: вспомогательную для логирования ETL процессов.

image

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

В результате получилась следующая реляционная модель:

Модель данных

image

Скрипт для создания объекта в базе данных sqlite:

DDL создание объекта

DROP TABLE IF EXISTS DIM_USER_AGENT;
CREATE TABLE DIM_USER_AGENT (
  DIM_USER_AGENT_ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  USER_AGENT_NK     TEXT NOT NULL DEFAULT 'n.a.',
  AGENT_OS          TEXT NOT NULL DEFAULT 'n.a.',
  AGENT_ENGINE      TEXT NOT NULL DEFAULT 'n.a.',
  AGENT_DEVICE      TEXT NOT NULL DEFAULT 'n.a.',
  AGENT_BOT         TEXT NOT NULL DEFAULT 'n.a.',
  UPDATE_DT         INTEGER NOT NULL DEFAULT 0,
  UNIQUE (USER_AGENT_NK)
);
INSERT INTO DIM_USER_AGENT (DIM_USER_AGENT_ID) VALUES (-1);

Стейдж

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

Формат лог файла:

//67.221.59.195 - - [28/Dec/2012:01:47:47 +0100] "GET /files/default.css HTTP/1.1" 200 1512 "https://project.edu/" "Mozilla/4.0"
//host ident auth time method request_nk protocol status bytes ref browser
$log_pattern = '/^([^ ]+) ([^ ]+) ([^ ]+) ([[^]]+]) "(.*) (.*) (.*)" ([0-9-]+) ([0-9-]+) "(.*)" "(.*)"$/';

Пропагация ключей

Когда сырые данные находятся в базе, нужно записать в таблицы измерений ключи, которых там нет. Тогда будет возможным построение ссылки на измерения. Например, в таблице DIM_REFERRER ключом является комбинация трёх полей.

SQL запрос пропагации ключей

/* Propagate the referrer from access log */
INSERT INTO DIM_REFERRER (HOST_NK, PATH_NK, QUERY_NK, UPDATE_DT)
SELECT
	CLS.HOST_NK,
	CLS.PATH_NK,
	CLS.QUERY_NK,
	STRFTIME('%s','now') AS UPDATE_DT
FROM (
	SELECT DISTINCT
	REFERRER_HOST AS HOST_NK,
	REFERRER_PATH AS PATH_NK,
	CASE WHEN INSTR(REFERRER_QUERY,'&sid')>0 THEN SUBSTR(REFERRER_QUERY, 1, INSTR(REFERRER_QUERY,'&sid')-1) /* отрезаем sid - специфика цмс */
	ELSE REFERRER_QUERY END AS QUERY_NK
	FROM STG_ACCESS_LOG
) CLS
LEFT OUTER JOIN DIM_REFERRER TRG
ON (CLS.HOST_NK = TRG.HOST_NK AND CLS.PATH_NK = TRG.PATH_NK AND CLS.QUERY_NK = TRG.QUERY_NK)
WHERE TRG.DIM_REFERRER_ID IS NULL

Пропагация в таблицу пользовательских агентов может содержать логику ботов, например, отрывок sql:


CASE
WHEN INSTR(LOWER(CLS.BROWSER),'yandex.com')>0
	THEN 'yandex'
WHEN INSTR(LOWER(CLS.BROWSER),'googlebot')>0
	THEN 'google'
WHEN INSTR(LOWER(CLS.BROWSER),'bingbot')>0
	THEN 'microsoft'
WHEN INSTR(LOWER(CLS.BROWSER),'ahrefsbot')>0
	THEN 'ahrefs'
WHEN INSTR(LOWER(CLS.BROWSER),'mj12bot')>0
	THEN 'majestic-12'
WHEN INSTR(LOWER(CLS.BROWSER),'compatible')>0 OR INSTR(LOWER(CLS.BROWSER),'http')>0
	OR INSTR(LOWER(CLS.BROWSER),'libwww')>0 OR INSTR(LOWER(CLS.BROWSER),'spider')>0
	OR INSTR(LOWER(CLS.BROWSER),'java')>0 OR INSTR(LOWER(CLS.BROWSER),'python')>0
	OR INSTR(LOWER(CLS.BROWSER),'robot')>0 OR INSTR(LOWER(CLS.BROWSER),'curl')>0
	OR INSTR(LOWER(CLS.BROWSER),'wget')>0
	THEN 'other'
ELSE 'n.a.' END AS AGENT_BOT

Таблицы агрегатов

В последнюю очередь будем грузить таблицы агрегатов, например, дневная таблица может загружаться следующим образом:

SQL запрос загрузки агрегата

/* Load fact from access log */
INSERT INTO FCT_ACCESS_USER_AGENT_DD (EVENT_DT, DIM_USER_AGENT_ID, DIM_HTTP_STATUS_ID, PAGE_CNT, FILE_CNT, REQUEST_CNT, LINE_CNT, IP_CNT, BYTES)
WITH STG AS (
SELECT
	STRFTIME( '%s', SUBSTR(TIME_NK,9,4) || '-' ||
	CASE SUBSTR(TIME_NK,5,3)
	WHEN 'Jan' THEN '01' WHEN 'Feb' THEN '02' WHEN 'Mar' THEN '03' WHEN 'Apr' THEN '04' WHEN 'May' THEN '05' WHEN 'Jun' THEN '06'
	WHEN 'Jul' THEN '07' WHEN 'Aug' THEN '08' WHEN 'Sep' THEN '09' WHEN 'Oct' THEN '10' WHEN 'Nov' THEN '11'
	ELSE '12' END || '-' || SUBSTR(TIME_NK,2,2) || ' 00:00:00' ) AS EVENT_DT,
	BROWSER AS USER_AGENT_NK,
	REQUEST_NK,
	IP_NR,
	STATUS,
	LINE_NK,
	BYTES
FROM STG_ACCESS_LOG
)
SELECT
	CAST(STG.EVENT_DT AS INTEGER) AS EVENT_DT,
	USG.DIM_USER_AGENT_ID,
	HST.DIM_HTTP_STATUS_ID,
	COUNT(DISTINCT (CASE WHEN INSTR(STG.REQUEST_NK,'.')=0 THEN STG.REQUEST_NK END) ) AS PAGE_CNT,
	COUNT(DISTINCT (CASE WHEN INSTR(STG.REQUEST_NK,'.')>0 THEN STG.REQUEST_NK END) ) AS FILE_CNT,
	COUNT(DISTINCT STG.REQUEST_NK) AS REQUEST_CNT,
	COUNT(DISTINCT STG.LINE_NK) AS LINE_CNT,
	COUNT(DISTINCT STG.IP_NR) AS IP_CNT,
	SUM(BYTES) AS BYTES
FROM STG,
	DIM_HTTP_STATUS HST,
	DIM_USER_AGENT USG
WHERE STG.STATUS = HST.STATUS_NK
  AND STG.USER_AGENT_NK = USG.USER_AGENT_NK
  AND CAST(STG.EVENT_DT AS INTEGER) > $param_epoch_from /* load epoch date */
  AND CAST(STG.EVENT_DT AS INTEGER) < strftime('%s', date('now', 'start of day'))
GROUP BY STG.EVENT_DT, HST.DIM_HTTP_STATUS_ID, USG.DIM_USER_AGENT_ID

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

Условие не даст загрузить ещё раз историю: CAST(STG.EVENT_DT AS INTEGER) > $param_epoch_from, где параметр является результатом запроса
‘SELECT COALESCE(MAX(EVENT_DT), ‘3600’) AS LAST_EVENT_EPOCH FROM FCT_ACCESS_USER_AGENT_DD’

Условие загрузит только полный день: CAST(STG.EVENT_DT AS INTEGER) < strftime(‘%s’, date(‘now’, ‘start of day’))

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

Отчёты

В сложных системах визуализации есть возможность создавать мета-модель на основе объектов базы данных, динамически управлять фильтрами и правилами агрегации. В конечном счёте, все приличные инструменты генерируют SQL запрос.

В данном примере мы создадим уже готовые SQL запросы и сохраним их в виде вью в базе данных — это и есть отчёты.

Визуализация

В качестве инструмента визуализации использовался Bluff: Beautiful graphs in JavaScript

Для этого потребовалось с помощью PHP пробежаться по всем репортам и сгенерировать html файл с таблицами.

$sqls = array(
'SELECT * FROM RPT_ACCESS_USER_VS_BOT',
'SELECT * FROM RPT_ACCESS_ANNOYING_BOT',
'SELECT * FROM RPT_ACCESS_TOP_HOUR_HIT',
'SELECT * FROM RPT_ACCESS_USER_ACTIVE',
'SELECT * FROM RPT_ACCESS_REQUEST_STATUS',
'SELECT * FROM RPT_ACCESS_TOP_REQUEST_PAGE',
'SELECT * FROM RPT_ACCESS_TOP_REQUEST_REFERRER',
'SELECT * FROM RPT_ACCESS_NEW_REQUEST',
'SELECT * FROM RPT_ACCESS_TOP_REQUEST_SUCCESS',
'SELECT * FROM RPT_ACCESS_TOP_REQUEST_ERROR'
);

Инструмент просто визуализирует таблицы результатов.

Вывод

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

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

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

Вернемся к теме измерения качества данных и автоматизации этого процесса.

Изучим проблемы технического окружения и обслуживания хранилищ данных, для чего реализуем сервер хранилища с минимальными ресурсами, например, на базе Raspberry Pi.

  • Введение
  • База данных
  • Модуль логирования
  • Поисковые роботы и реферралы
  • Расширение модуля
  • Модернизация программного кода

Практически у каждого владельца сайта в какой-то момент возникает желание узнать какие-то статистические данные о своих посетителях. Сколько посетителей заглядывает на сайт, как долго они находятся на сайте, какие страницы смотрят, откуда приходят и т.д. И, в принципе, для получения подобной информации хватает разнообразных бесплатных (и не очень) сервисов типа top.mail.ru или www.spylog.com, а так же парсеров логов веб серверов. Но все эти службы и сервисы имеют немало недостатков – например потеря информации из-за недоступности онлайн службы или блокировки пользователем изображений в своем браузере или же избыточность лог файлов веб сервера и невозможность более-менее однозначно с помощью парсинга лог файлов определить сессию пользователя. А кроме того они совершенно неспособны дать ответы на какие-то специфические вопросы, например, на вопрос «показать список пользователей, пришедших с поисковых систем и зарегистрировавшихся в сессию прихода». Или же «показать все сессии конкретного пользователя за весь период времени». Что приводит постепенно к мысли о создании собственной системы логирования и набора отчетов для получения нужной статистики.

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

На самом деле сам по себе процесс логирования захода посетителя на сайт ничего сложного из себя не представляет. Есть страницы (таблица Page), идентифицируемые их виртуальным адресом и сайтом, есть просмотры пользователями этих страниц (таблица Request) и есть сессии пользователей (совокупность просмотренных пользователем страниц сайта). Соответственно для сохранения всех этих данных у меня получилась вот такая база:

База данных

Небольшое пояснение – как я раньше уже упоминал в списке отчетов по посетителям у меня немаловажную роль играли зарегистрированные пользователи. Что и побудило меня добавить столбец UserID, куда будет записываться имя пользователя если логируется аутентифицированный запрос. И хотя я, например, предпочитаю использовать для идентификации пользователя целое число (identity ключ соотв. таблицы базы) в таблице Session это поле имеет тип varchar для максимальной совместимости. Кроме того в сессии сохраняется информация о первой и последней страницах сессии, а так же о дате начала и окончания сессии для удобства построения отчетов по точкам входа/выхода и времени. Ну а с каждым запросом страницы я сохраняю информацию о том, аутентифицирован ли пользователь и является ли этот запрос постбеком.

Для записи запосов в БД используются две не самые сложные процедуры:

create procedure p_SessionStart 
     @UserID           int = null, 
     @IPAddress        varchar(15), 
     @BrowserString    varchar(1024), 
     @ReferralURL      varchar(4096) = null, 
     @Site                varchar(100) = null, 
     @PageUrl          varchar(255) 
  as 
  begin tran 
  declare @PageID int 
  select @PageID = PageID from Page where PageURL = @PageUrl and (@Site is null and Site = @Site or Site is null) 
  if @PageID is null 
  begin 
     insert into Page (PageURL, Site) values (@PageUrl, @Site) 
     select @PageID = @@IDENTITY 
  end 
  insert into Session (UserID, DateStart, FirstPageID, LastPageID, DateEnd, IPAddress, BrowserString, ReferralURL) 
     values (@UserID, getDate(), @PageID, @PageID, getDate(), @IPAddress, @BrowserString, @ReferralURL) 
  commit 
  return @@identity 
  go 
  create procedure p_DoRequest 
     @SessionID  int, 
     @UserID     varchar(100) = null, 
     @Site          varchar(100) = null, 
     @PageUrl    varchar(255), 
     @QueryString   varchar(1024), 
     @IsPostBack    bit, 
     @IsAuthenticated     bit 
  as 
  begin tran 
  declare @PageID int 
  select @PageID = PageID from Page where PageURL = @PageUrl and (@Site is null and Site = @Site or Site is null) 
  if @PageID is null 
  begin 
     insert into Page (PageURL, Site) values (@PageUrl, @Site) 
     select @PageID = @@IDENTITY 
  end 
  insert into Request (PageID, SessionID, RequestDate, QueryString, IsPostBack, IsAuthenticated) 
     values (@PageID, @SessionID, getDate(), @QueryString, @IsPostBack, @IsAuthenticated) 
  update  
     Session 
  set 
     LastPageID = @PageID,  
     DateEnd = getdate() 
  where  
     SessionID = @SessionID 
  if @UserID is not null 
     update  
        Session 
     set 
        UserID = @UserID 
     where  
        SessionID = @SessionID 
  commit 
  go

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

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

Программную часть системы логирования посещений сайтов я решил сделать в виде HTTP модуля. Предпосылки очевидны – система логирования должна работать с сайтами без изменения их кода, просто настраиваться и обрабатывать все asp.net запросы. Опять таки код этого модуля прост до безобразия и в сумме занимает едва за сотню строк кода.

Так как вся настройка работы модуля будет вестись с помощью конфигурационного файла, то самым оптимальным решением для этого будет создание своей секции в конфигурационного файла для модуля. Модулю для своей работы нужна строка подключения к БД, имя сайта (необязательно) и место хранения идентификатора сессии пользователя (asp.net сессия или куки, по умолчанию куки). Соответственно код структуры параметров модуля:

public enum PersistIDPlace 
  { 
        Session, 
        Cookie 
  } 
  public class SiteStatsSettings 
  { 
        public string ConnectionString; 
        public string Site; 
        public PersistIDPlace SessionIDPlace; 
  }

Сама же секция конфигурационного файла суть класс, реализующий интерфейс System.Configuration.IConfigurationSectionHandler. Этот интерфейс содержит единственный метод Create, который должен вернуть объект. Я не буду долго рассказывать что и как здесь необходимо сделать (это можно прочитать и в MSDN) и просто приведу код класса:

public class SiteStatsConfigHandler : IConfigurationSectionHandler 
  { 
        public SiteStatsConfigHandler(){} 
        public object Create(object parent, object configContext, System.Xml.XmlNode section) 
        { 
              SiteStatsSettings ret = new SiteStatsSettings(); 
              ret.ConnectionString = section.SelectSingleNode("ConnectionString").InnerText; 
              ret.Site = section.SelectSingleNode("Site") != null ? section.SelectSingleNode("Site").InnerText : ""; 
              if(section.SelectSingleNode("SessionIDPlace") != null) 
                    ret.SessionIDPlace = (PersistIDPlace) Enum.Parse(typeof(PersistIDPlace), section.SelectSingleNode("SessionIDPlace").InnerText); 
              else 
                    ret.SessionIDPlace = PersistIDPlace.Cookie; 
              return ret; 
        } 
  }

Как видите ничего сложного в вышеприведенном коде нет – он всего лишь читает параметры из XmlNode и заполняет класс параметров модуля. Для того, чтобы добавить созданную выше секцию в конфигурационном файле в секции <configuration> теперь достаточно добавить вот такие строки:

<configSections> 
  <section name="siteStats" type="SiteStats.SiteStatsConfigHandler, SiteStats"/> 
  </configSections> 
  И теперь можно использовать секцию <siteStats> для задания параметров модуля: 
  <siteStats> 
        <ConnectionString>server=localhost;uid=sa;pwd=;database=SiteStats</ConnectionString> 
        <Site>Mania</Site> 
        <SessionIDPlace>Cookie</ SessionIDPlace> 
  </siteStats>

А получить эти параметры в коде класса можно с помощью следующей строки кода:

SiteStatsSettings settings = (SiteStatsSettings) ConfigurationSettings.GetConfig("siteStats");

Теперь осталось всего ничего – реализовать класс для записи в БД и собственно сам HTTP модуль. Код класса для работы с БД настолько банален, что их можно привести даже без комментариев:

public class SiteStatsBLL 
  { 
        private static int SessionStart(HttpContext context) 
        { 
              SiteStatsSettings settings = (SiteStatsSettings) ConfigurationSettings.GetConfig("siteStats"); 
              SqlConnection myConn = new SqlConnection(settings.ConnectionString); 
              SqlCommand myCmd = new SqlCommand("p_SessionStart", myConn); 
              myCmd.CommandType = CommandType.StoredProcedure; 
              if (context.User.Identity.IsAuthenticated) 
                    myCmd.Parameters.Add("@UserID", Int32.Parse(context.User.Identity.Name)); 
              myCmd.Parameters.Add("@IPAddress", context.Request.UserHostAddress); 
              myCmd.Parameters.Add("@BrowserString", context.Request.UserAgent == null ? "" : context.Request.UserAgent); 
              if (context.Request.UrlReferrer != null) 
                    myCmd.Parameters.Add("@ReferralURL", context.Server.UrlDecode(context.Request.UrlReferrer.ToString())); 
              if (settings.Site != "") 
                    myCmd.Parameters.Add("@Site", settings.Site); 
              myCmd.Parameters.Add("@PageURL", context.Request.FilePath); 
              myCmd.Parameters.Add("RETURN_VALUE", SqlDbType.Int); 
              myCmd.Parameters["RETURN_VALUE"].Direction = ParameterDirection.ReturnValue; 
              myConn.Open(); 
              myCmd.ExecuteNonQuery(); 
              myConn.Close(); 
              return (int) myCmd.Parameters["RETURN_VALUE"].Value; 
        } 
        public static void Request(HttpContext context) 
        { 
              SiteStatsSettings settings = (SiteStatsSettings) ConfigurationSettings.GetConfig("siteStats"); 
              int SessionID; 
              switch(settings.SessionIDPlace) 
              { 
                    case PersistIDPlace.Session: 
                    if (context.Session["SessionID"] != null) 
                          SessionID = (int) context.Session["SessionID"]; 
                    else 
                    { 
                          SessionID = SessionStart(context); 
                          context.Session["SessionID"] = SessionID; 
                    } 
                          break; 
                    default: 
                          if (context.Request.Cookies["SessionID"] != null) 
                               SessionID = Int32.Parse(context.Request.Cookies["SessionID"].Value); 
                          else 
                          { 
                               SessionID = SessionStart(context); 
                               context.Response.Cookies.Add(new HttpCookie("SessionID", SessionID.ToString())); 
                          } 
                          break; 
              } 
              SqlConnection myConn = new SqlConnection(settings.ConnectionString); 
              SqlCommand myCmd = new SqlCommand("p_DoRequest", myConn); 
              myCmd.CommandType = CommandType.StoredProcedure; 
              myCmd.Parameters.Add("@SessionID", SessionID); 
              if (context.User.Identity.IsAuthenticated) 
                    myCmd.Parameters.Add("@UserID", context.User.Identity.Name); 
              if (settings.Site != "") 
                    myCmd.Parameters.Add("@Site", settings.Site); 
              myCmd.Parameters.Add("@PageURL", context.Request.RawUrl.IndexOf("?") != -1 ? context.Request.RawUrl.Substring(0, context.Request.RawUrl.IndexOf("?")) : context.Request.RawUrl); 
              myCmd.Parameters.Add("@QueryString", context.Request.QueryString.ToString()); 
              myCmd.Parameters.Add("@IsAuthenticated", context.Request.IsAuthenticated); 
              myCmd.Parameters.Add("@IsPostBack", context.Request.HttpMethod == "POST"); 
              myConn.Open(); 
              myCmd.ExecuteNonQuery(); 
              myConn.Close(); 
        } 
  }

Для логирования запроса пользователя используется метод SiteStatsBLL.Request(), который в свою очередь при необходимости вызывает метод SiteStatsBLL.SessionStart() для старта новой сессии. Оба метода всего лишь вызывают соответствующие хранимые процедуры.

Метод SiteStatsBLL.Request() я вызываю в обработчике события HttpApplication. PostRequestHandlerExecute. В момент срабатывания этого события обработка стнаницы завершена и страница готова к отправке клиенту, но сессия еще сущетвует (у нас же есть опция сохранения SessionID в сессии). И вся задача HTTP модуля состоит в том, чтобы при наступлении этого события вызвать метод логирования запроса:

public class SiteStatsModule : IHttpModule 
  { 
        void IHttpModule.Init(HttpApplication context) 
        { 
              context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute); 
        } 
        void IHttpModule.Dispose() 
        { 
        } 
        void context_PostRequestHandlerExecute(object sender, EventArgs e) 
        { 
              SiteStatsBLL.Request(HttpContext.Current); 
        } 
  }

Все… процесс создания модуля логирования запросов к сайту завершен. Теперь для того, чтобы начать вести логи для какого-то сайта достаточно переписать получившуюся сборку в подкаталог bin этого сайта и внести в файл web.config следуюшие изменения.

1. Добавить описание секции в раздел <configuration>:

<configSections>
          <section name="siteStats" type="SiteStats.SiteStatsConfigHandler, SiteStats"/>
  </configSections>

2. В тот же раздел добавить саму секцию siteStats:

<siteStats> 
        <ConnectionString>строка подключения к БД</ConnectionString> 
        <Site>имя сайта</Site> 
        <SessionIDPlace>Место хранения идентификатора сессии (Cookie или Session)</SessionIDPlace> 
  </siteStats>

3. В секцию <system.web> добавить описание модуля:

<httpModules> 
        <add name="SiteStatsModule" type="SiteStats.SiteStatsModule, SiteStats"/> 
  </httpModules>

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

Написанный выше модуль в отличие от бесплатных систем интернет статистики логирует любые заходы на страницу – как заходы обычных пользователей, так и заходы всяких поисковых роботов или систем автоматического сохранения сайтов. И естесственно если не делать дополнительной фильтрации для выявления ненужных заходов, то можно получить цифры, которые будут весьма и весьма далеки от настоящих. Например для сайта aspnetmania.com 1 декабря 2005 года количество залогированных модулем посетителей (сессий) было порядка 26 тысяч, при этом статистика на top.mail.ru показывает 1545 посетителей. Остальные запросы – активная работа поисковых роботов :). И хочешь – не хочешь, но их нужно будет каким-то образом для себя отмечать (или вообще убирать в случае, если статистика по заходам роботов не интересна). Для этого во первых нужно сделать поддержку списка исключаемых юзерагентов ну и, во вторых, добавить статусное поле в таблицу Session.

Дабы не утруждать читателя долгими рассуждениями о том, какие существуют роботы и какой робот кому служит я приведу сразу небольшой список масок учитываемых мной юзерагентов роботов:

Источник Маска
Google Googlebot%
Mediapartners-Google%
Yahoo Yahoo%
% Yahoo! Slurp%
MSN msnbot%
Yandex Yandex%
Rambler StackRambler%
Aport Aport%
Teleport Pro Teleport Pro%
Прочие %bot%

Последняя строка в этом перечне конечно довольно таки радикальная, но этих роботов столько человеческая мысль наплодила – благо, что хоть они зачастую признаются честно, что они боты :).

Кроме обработки «непользовательских» заходов нужно также сделать обработку реферралов – с какого сайта/поисковой системы пришел посетитель и какие поисковые слова он использовал в случае прихода с поисковой системы. И тут опять не обойтись без дополнительных таблиц – списка поисковых систем и для списка реферральных сайтов. Ну а кроме того необходима таблица для хранения поисковых фраз. И, естесственно, все эти таблицы будут связаны с таблицей Session.

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

Более интересна таблица поисковых систем – в ней кроме названия поисковой системы понадобится также сохранять информацию о маске имени сайта, а так же информацию о том, где в строке реферрала хранится поисковая фраза. Обычно поисковая фраза содержится в каком-то конкретном параметре URL, но бывают и особо “продвинутые” индивидуумы, засовывающие ее в структуру каталогов URL. Но и на их фоне выделается AOL, в случае поиска с главной страницы передающий поисковую фразу в строке запроса в зашифрованном виде. Благо хоть в другом параметре, что позволяет упростить учет поисковых фраз.

Ниже представлен примерный справочник поисковых сайтов для создаваемой системы статистики.

Поисковик Маска URL Параметр поисковой фразы
Google %google% q=
Yahoo %yahoo% p=
MSN Search %search.msn% q=
Altavista %altavista% q=
AOL Search %search.aol% query=
Alexa %alexa.com% q=
AllTheWeb %alltheweb.com% q=
AskJeeves %ask.com% q=
HotBot %hotbot.com% query=
Jayde %jayde.com% query=
LookSmart %looksmart.com% qt=
Lycos %lycos.com% query=
Netscape %netscape.com% query=
Overture %overture.com% Keywords=
Teoma %teoma.com% q=
WiseNut %wisenut.com% q=
A9 %a9.com% /
WebCrawler %webcrawler.com% /
Business %business.com% query=
DogPie %dogpile.com% /
EntireWeb %entireweb.com% q=
Excite %excite.com% /
Gigablast %gigablast.com% q=
Infospace %infospace.com% /
Mamma %mamma.com% query=
Metacrawler %metacrawler.com% /
SplatSearch %splatsearch.com% searchstring=
Yandex %yandex.ru/yandsearch% text=
Aport %sm.aport.ru% r=
Rambler %search.rambler.ru% words=

Ввиду того, что основная масса «неправильных» поисковиков редко используется даже в США (откуда они все родом) и приход на русскоязычный сайт с этого поисковика стремится к нулю, я поленился делать разбор подобных адресов дабы не усложнять логику работы программы.

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

Кроме упомянутых мною выше 4-х новых таблиц (Sites, Bots, SearchEngines и Keywords) меняется только таблица Session, в которой добавляются 4 поля, ссылающиеся на добавленные таблицы:

Расширение модуля

Ну и соответствующим образом меняется процедура старта сессии:

ALTER procedure p_SessionStart 
     @UserID           int = null, 
     @IPAddress        varchar(15), 
     @BrowserString    varchar(1024), 
     @ReferralURL      varchar(4096) = null, 
     @Site                varchar(100) = null, 
     @PageUrl          varchar(255), 
     @BotID               int = null, 
     @SiteName            varchar(255) = null, 
     @Keyword             varchar(1000) = null, 
     @SearchEngineID      int = null 
  as 
  begin tran 
  declare @PageID int 
  select @PageID = PageID from Page where PageURL = @PageUrl and (@Site is null and Site = @Site or Site is null) 
  if @PageID is null 
  begin 
     insert into Page (PageURL, Site) values (@PageUrl, @Site) 
     select @PageID = @@IDENTITY 
  end 
  declare @SiteID   int 
  if @SiteName is not null 
  begin 
        select @SiteID = SiteID from Sites where Name = @SiteName 
        if @SiteID is null 
        begin 
              insert into Sites (Name) values (@SiteName) 
              select @SiteID = @@IDENTITY 
        end 
  end 
  declare @KeywordID      int 
  if @Keyword is not null 
  begin 
        select @KeywordID = KeywordID from Keywords where Keywords = @Keyword 
        if @KeywordID is null 
        begin 
              insert into Keywords (Keywords) values (@Keyword) 
              select @KeywordID = @@IDENTITY 
        end 
  end 
  insert into Session (UserID, DateStart, FirstPageID, LastPageID, DateEnd, IPAddress, BrowserString, ReferralURL, BotID, SiteID, SearchEngineID, KeywordID) 
     values (@UserID, getDate(), @PageID, @PageID, getDate(), @IPAddress, @BrowserString, @ReferralURL, @BotID, @SiteID, @SearchEngineID, @KeywordID) 
  commit 
  return @@identity

На самом деле заполнять дополнительное данные о роботах, реферралах и поисковых системах можно 2-мя путями – в момент добавления записи в БД или отдельной хранимой процедурой, обрабатывающей уже сохраненные в базу данные. Соответственно во втором случае изменение хранимой процедуры для добавления записи о сессии не нужно. И, так как этот путь немного проще, я сначала покажу его реализацию.
Обработка логов в БД

Как я уже упомянул, эта процедура должна обрабатывать уже введенные данные. У меня это были данные, введенные за какой-то последний отрезок времени (конкретней – за последний час). Соответственно одним из параметров этой процедуры будет дата/время, с которой нужно обрабатывать данные. И, кроме того, также в нее передается параметр каким образом обрабатывать записи роботов – связывать их или же удалять

ALTER PROCEDURE p_Session_Fill 
        @DateStart  datetime = null, 
        @ClearBots  bit = 0 
  AS 
  if @DateStart is null 
        set @DateStart = dateadd(hh, -1, getDate())

Обработка записей роботов проста до примитивизма. При сохранении логов заходов роботов делается 2 простых update по связке таблиц Session и Bots (второй запрос нужен для того, чтобы обработать “невыясненных” роботов, милостиво соизволивших сообщить о себе, что они таки роботы). При очистке же базы от записей заходов роботов – две не менее простых команды delete.

if @ClearBots = 0 
  begin 
        update  
              Session  
        set  
              BotID = Bots.BotID  
        from 
              Bots 
        where  
              Session.BotID is null and DateStart > @DateStart and Mask <> '%bot%' and BrowserString like Mask 
        update  
              Session  
        set  
              BotID = Bots.BotID  
        from 
              Bots 
        where  
              Session.BotID is null and Mask = '%bot%' and DateStart > @DateStart and BrowserString like '%bot%' 
  end 
  else         
  begin 
        delete from Request where SessionID in (select SessionID from Session inner join Bots on BrowserString like Mask) 
        delete from Session where SessionID in (select SessionID from Session inner join Bots on BrowserString like Mask) 
  end

Не бОльшую сложность представляет собой и запрос по определению заходов с поисковых систем

update  
        Session  
  set  
        SearchEngineID = SearchEngines.SearchEngineID  
  from 
        SearchEngines 
  where  
        ReferralURL is not null and DateStart > @DateStart and ReferralURL like SearchMask

В реферралы попадают все не попавшие в предыдущий запрос записи с непустым полем ReferralURL. При этом нужно получить только адрес сайта, с которого пришел реферрал.

declare @ReferralUrl varchar(4096) 
  declare @SessionID      int 
  declare @SiteName varchar(255) 
  declare @SiteID int 
  declare cur1 cursor for 
        select SessionID, ReferralURL from Session with (nolock) 
              where ReferralURL is not null and DateStart > @DateStart and SiteID is null 
  open cur1 
  fetch next from cur1 into @SessionID, @ReferralUrl 
  while @@fetch_status = 0 
  begin 
        set @SiteID = null 
        set @SiteName = substring(@ReferralUrl, 8, len(@ReferralUrl) - 7) 
        set @SiteName = substring(@SiteName, 1, charindex('/', @SiteName) - 1) 
        select @SiteID = SiteID from Sites with (nolock) where Name = @SiteName 
        if @SiteID is null 
        begin 
              insert into Sites(Name) 
                    values (@SiteName) 
              set @SiteID = @@identity 
        end 
        update 
              Session 
        set 
              SiteID = @SiteID 
        where 
              SessionID = @SessionID 
        fetch next from cur1 into @SessionID, @ReferralUrl 
  end 
  close cur1 
  deallocate cur1

Ну и последний момент – выделение поисковых запросов из уже найденных приходов с поисковых систем. Для этого как раз таки и нужно поле KeywordMask – все, что будет найдено в строке между подстрокой их этого поля и символом & (или до конца строки, если такого символа больше нет) считается поисковой фразой. При этом, как я уже упомянул ранее, запросы, в которых поисковые фразы не выделяются параметрами URL, не учитываются (для запросов с таких поисковых систем значение KeywordMask равно /).

declare @SearchEngineID int 
  declare @KeywordLabel varchar(10) 
  declare cur2 cursor for 
        select  
              SessionID,  
              Session.SearchEngineID,  
              ReferralURL,  
              KeywordsMask 
        from 
              Session with (nolock) inner join SearchEngines with (nolock) on Session.SearchEngineID = SearchEngines.SearchEngineID 
        where 
              KeywordID is null and DateStart > @DateStart 
  open cur2 
  fetch next from cur2 into @SessionID, @SearchEngineID, @ReferralUrl, @KeywordLabel 
  while @@fetch_status = 0 
  begin 
        declare @keywords varchar(1000) 
        declare @pos int 
        set @pos = null 
        set @keywords = null 
        set @pos = CHARINDEX(@KeywordLabel, @ReferralUrl) 
        if @pos > 0 
        begin 
              set @keywords = SUBSTRING(@ReferralUrl, @pos + len(@KeywordLabel), len(@ReferralUrl) - len(@KeywordLabel) - @pos + 1) 
              if @KeywordLabel <> '/' 
              begin 
                    set @pos = CHARINDEX('&', @keywords) 
                    if @pos > 0 
                          set @keywords = substring(@keywords, 1, @pos - 1) 
              end 
              else 
              begin 
                    while @pos <> 0 
                    begin 
                          set @keywords = SUBSTRING(@keywords, @pos + 1, len(@keywords) - @pos) 
                          set @pos = CHARINDEX('/', @keywords) 
                    end 
              end 
        end 
        if @keywords is not null and @keywords <> '' 
        begin 
              set @keywords = REPLACE(@keywords, '+', ' ') 
              set @keywords = REPLACE(@keywords, '%20', ' ') 
              set @keywords = ltrim(rtrim(@keywords)) 
              if @keywords <> '' 
              begin 
                    declare @KeywordID int 
                    set @KeywordID = null 
                    select @KeywordID = KeywordID from Keywords with (nolock) where Keywords = @keywords 
                    if @KeywordID is null 
                    begin 
                          insert into Keywords (Keywords) 
                               values (@keywords) 
                          set @KeywordID = @@identity 
                    end 
                    update 
                          Session 
                    set 
                          KeywordID = @KeywordID 
                    where 
                          SessionID = @SessionID 
              end 
        end 
        fetch next from cur2 into @SessionID, @SearchEngineID, @ReferralUrl, @KeywordLabel 
  end 
  close cur2 
  deallocate cur2

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

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

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

В конфигурацию модуля добавим 2 дополнительных параметра – ParseAddData для задания факта дополнительной программной обработки пользовательских запросов и PersistBots для указания сохранять ли в базе запросы роботов. Для этого в класс SiteStatsSettings также добавим строки

public bool ParseAddData;
  public bool PersistBots;

Ну а в код метода Create класса SiteStatsConfigHandler соответственно добавим их инициализацию

ret.ParseAddData = section.SelectSingleNode("ParseAddData") != null ? bool.Parse(section.SelectSingleNode("ParseAddData").InnerText) : false;
  ret.PersistBots = section.SelectSingleNode("PersistBots") != null ? bool.Parse(section.SelectSingleNode("PersistBots").InnerText) : true;

Теперь конфигурационная секция модуля в файле web.config будет выглядеть примерно так.

<siteStats> 
              <ConnectionString>server=localhost;uid=sa;pwd=;database=SiteStats</ConnectionString> 
              <SessionIDPlace>Cookie</SessionIDPlace> 
              <ParseAddData>true</ParseAddData> 
              <PersistBots>true</PersistBots> 
  </siteStats>

Для поиска и определения идентификаторов роботов и поисковых систем в коде SiteStatsBLL добавляются 2 статических поля типа SortedList для хранения списка шаблонов роботов и поисковых сайтов и статический конструктор для заполнения этих списков. При этом для удобства поиска в этих списках будем хранить не сами маски для поисков, а сразу же создадим соответствующие экземпляры класса Regex. И так как поиск ведется до первого найденного значения не нужно забывать о том, что порядок размещения масок важен (поиск по маске “bot” должен производиться в последнюю очередь)

private static SortedList bots; 
  private static SortedList searchEngines; 
  private class SearchEngine 
  { 
        public Regex Mask; 
        public string Key; 
        public SearchEngine(Regex mask, string key) 
        { 
              Mask = mask; 
              Key = key; 
        } 
  } 
  static SiteStatsBLL() 
  { 
        bots = new SortedList(); 
        SiteStatsSettings settings = (SiteStatsSettings) ConfigurationSettings.GetConfig("siteStats"); 
        SqlConnection myConn = new SqlConnection(settings.ConnectionString); 
        SqlCommand myCmd = new SqlCommand("select * from Bots", myConn); 
        myConn.Open(); 
        SqlDataReader rdr = myCmd.ExecuteReader(); 
        while(rdr.Read()) 
        { 
              Regex mask = new Regex(rdr["Mask"].ToString().Replace("%", "(.+?)"), RegexOptions.Compiled | RegexOptions.IgnoreCase); 
              bots.Add((int) rdr["BotID"], mask); 
        } 
        rdr.Close(); 
        searchEngines = new SortedList(); 
        myCmd.CommandText = "select * from SearchEngines"; 
        rdr = myCmd.ExecuteReader(); 
        while(rdr.Read()) 
        { 
              Regex mask = new Regex(rdr["SearchMask"].ToString().Replace("%", "(.+?)"), RegexOptions.Compiled | RegexOptions.IgnoreCase); 
              bots.Add((int) rdr["SearchEngineID"], new SearchEngine(mask, rdr["KeywordMask"].ToString())); 
        } 
        rdr.Close(); 
        myConn.Close(); 
  }

В код метода SessionStart добавим блок для определения идентификаторов робота и поисковой системы, а так же поисковой фразы и сайта-реферрала

int BotID = 0; 
  int SearchEngineID = 0; 
  string SiteName = ""; 
  string Keyword = ""; 
  if(settings.ParseAddData) 
  { 
        foreach(object key in bots.Keys) 
              if(((Regex) bots[key]).IsMatch(context.Request.UserAgent == null ? "" : context.Request.UserAgent)) 
              { 
                    BotID = (int) key; 
                    break; 
              } 
        if(BotID != 0 && !settings.PersistBots) 
              return -1; 
        if(context.Request.UrlReferrer != null && context.Request.UrlReferrer.ToString() != "") 
        { 
              foreach(object key in searchEngines.Keys) 
                    if(((SearchEngine) searchEngines[key]).Mask.IsMatch(context.Request.UrlReferrer.ToString())) 
                    { 
                          SearchEngineID = (int) key; 
                          foreach(string param in context.Request.UrlReferrer.Query.Split('&')) 
                               if(param.StartsWith(((SearchEngine) searchEngines[key]).Key)) 
                               { 
                                     Keyword = param.Replace(((SearchEngine) searchEngines[key]).Key, ""); 
                                     break; 
                               } 
                          break; 
                    } 
              if(SearchEngineID == 0) 
                    SiteName = context.Request.UrlReferrer.Host; 
        } 
  }

Обратите внимание, что в случае, если система настроена на игнорирование запросов поисковых роботов, то этот метод вернет -1. Соответственно в методе Request дополнительно нужно сделать проверку значения SessionID перед записью информации о запросе в базу (сразу после секции определения SessionID).

if(SessionID == -1)
        return;

Ну и, наконец, в том же методе SessionStart нужно добавить код для передачи при необходимости найденных значений в хранимую процедуру

if(settings.ParseAddData) 
  { 
        if (BotID != 0) 
              myCmd.Parameters.Add("@BotID", BotID); 
        if (SearchEngineID != 0) 
              myCmd.Parameters.Add("@SearchEngineID", SearchEngineID); 
        if (Keyword != "") 
              myCmd.Parameters.Add("@Keyword", Keyword); 
        if (SiteName != "") 
              myCmd.Parameters.Add("@SiteName", SiteName); 
  }

Все, теперь работа по созданию модуля логирования точно завершена :). В прилагающемся к статье архиве можно найти mdf файл базы данных и весь исходный код. Ну а я же в следующей статье постараюсь добавить жизни в этот проект и опишу создание набора отчетов для получения более любимых руководством и/или заказчиками картинок и таблиц с использованием Reporting Services.

  1. Зачем нужна веб-аналитика сайта
    • 1. Каковы цели аналитики
    • 2. Какие задачи выполняет аналитика сайта
  2. Виды веб-аналитики
    • 1. Комплексная веб-аналитика
    • 2. Сквозная веб-аналитика
  3. Инструменты веб-аналитики
    • 1. Google Analytics
    • 2. Яндекс.Метрика
  4. Узнать статистику сайта: подведем итоги

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

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

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

Главное – определить, с каким намерением посетитель пришел на сайт. Да, если человек уже оформил заказ, то очевидно, зачем он там находился. А если этот посетитель только думает о покупке, то дальнейшие действия неизвестны.

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

Чтобы определить, проявляют ли посетители интерес к продукту, нужно посмотреть, какие страницы они посещали. Например, у вас есть раздел под названием “Блог”, где написано много полезного. Он привлекает аудиторию, но не все читатели блога заинтересованы в покупке.

  • Во-вторых, обеспечить высокий ROI, то есть коэффициент рентабельности вложений. Иными словами, только статистика позволит определить, оправданы ли расходы на продвижение. Рассчитывается он так: доход — себестоимость / инвестиции * 100%.

Помогает понять, какие страницы сайта неэффективны

Они либо не привлекают посетителей, либо привлекают только тех, кто продукт не приобретает.

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

Магазин снизил цену на оба платья. Первое платье быстро раскупили, а второе – нет. Компания могла заранее понять, что модель 1 приобретать еще можно, а модель 2 крайне непопулярна. К тому же стоит сделать разные скидки на них при распродаже.

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

Какая статья была бы в этом случае полезной? Такая, в которой речь идет уже о готовом предмете. Можно дать советы по выбору стола для кухни / гостиной, перечислить размеры, материалы.

У людей была потребность в столах, но приобретать его не планировали. У них другое намерение – избежать покупки.

Посетители находят один и тот же сайт с помощью разных поисковых запросов. На их основе создается список ключевых слов. Затем ключи распределяются по страницам. Это один из этапов SEO-продвижения, в которое входит анализ семантического ядра.

Важно, что с течением времени данные о запросах устаревают. Аналитика – способ понять, какие ключевые слова не действенны, какие, наоборот, стоит расширить. Причины разные:

  • тема потеряла актуальность;
  • тот же смысл стал чаще выражаться по-другому, например, допустим, что запрос шапка шарф купить стал популярнее, чем купить комплект шапка и шарф

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

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

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

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

виды веб-аналитики: комплексная и сквозная

Выделяются на основании того, что подвергается исследованию.

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

Комплексная веб-аналитика проводится при следующих случаях:

  • Определить эффективность рекламы;
  • Получить реакцию посетителей на интерфейс сайта;
  • Определить категорию аудитории (предприниматели, служащие, мамы в декрете, студенты и.т.д.);
  • Выявить каналы привлечения посетителей.

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

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

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

Какие выводы можно сделать из этого примера? Прямые запросы, то есть такие, где прямо указано название компании – это не всегда следствие рекламы вне сети или сарафанного радио.

Рассмотрим их важные показатели, а также, как подключить сервисы к сайту.

Перед началом работы вам потребуется:

  • зарегистрироваться;
  • подключить счетчик сайта.

Создаем учетную запись Google, если ее еще нет. Это будет предложено сделать в ходе регистрации. Далее называем аккаунт. 

Внимание: аккаунт не равен общей учетной записи в этом случае. Один пользователь Google может создать до 100 аккаунтов. За каждым из них закрепляется максимум несколько сайтов.

Зачем такие сложности? Чтобы было удобно вести много проектов. По этой же причине необходимо указать отрасль. Если вы заранее знаете, что точно не запутаетесь, ее можно выбрать наобум.

Правда, некоторым понадобится сравнение статистики по отраслям, чтобы понять, какие из них оказались более востребованными.

Теперь предстоит подключить сайт, то есть изменить его код так, чтобы у сервиса появился доступ к данным сайта. Как это сделать? Есть три способа:

  • вставить код счетчика в HTML-код сайта;
  • войти в Диспетчер тегов Google;
  • при работе с WordPress установить соответствующий плагин.

На главной странице сразу представлены основные показатели:

  • количество сеансов, а именно посещений;
  • средняя длительность сеанса, то есть как долго обычно люди остаются на сайте;
  • показатель отказов – сколько посетителей не перешли с посадочной страницы в другие разделы сайта, либо быстро покинули сайт;
  • конверсия – соотношение количества посетителей с теми, кто совершил целевые действия, например нажал на кнопку.

Отдельно можно настроить отчеты по конкретным показателям.

Для прохождения регистрации придется создать почту в Яндекс, если у вас ее еще нет.

Находим раздел Счетчики. Далее нажимаем на Добавить счетчик.  Придумываем ему любое название, значения это иметь не будет. Затем вписываем URL.

Связать счетчик и сайт можно такими же тремя способами. Диспетчер тегов Google позволяет работать и с Яндекс.Метрикой. Кроме того, для нее есть подходящий плагин в WordPress.

В отличие от предыдущего сервиса, в Яндекс.Метрике есть Вебвизор. Эта функция позволяет увидеть поведение посетителей.

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

Теперь вы знаете, как подключить к сервисам аналитики сайт, на какие показатели следует обращать внимание в первую очередь.

Для получения информации о посещаемости вашего сайта помимо инструмента “Отчеты” в конструкторе сайтов, вы можете пользоваться такими сервисами как Яндекс.Метрика и Google Analytics.

Получение кода статистики

Яндекс.Метрика

  1. Зарегистрируйтесь в сервисе Яндекс.Метрика;
  2. Войдите в систему Яндекс.Метрика;
  3. Нажмите на кнопку “Добавить счетчик”;
  4. Введите название и адрес вашего сайта;
  5. Нажмите на кнопку “Продолжить” и вы перейдете на страницу “Код счетчика”;
  6. Скопируйте код статистики для добавления на ваш сайт.

Google Analytics

  1. Зарегистрируйтесь в системе Google Analytics
  2. Перейдите на страницу регистрации нового сайта;
  3. Введите название вашего сайта, адрес и место нахождения вашего сайта.
  4. Нажмите продолжить и введите ваши контактные данные. После принятия лицензинного соглашения вы получите код статистики для добавления на ваш сайт.

Добавление кода статистики на сайт

  1. Зайдите в раздел “Настройки” конструктора сайта;
  2. Нажмите на ссылку “Код для статистики”;
  3. Вставьте код статистики, который вы получили на Google Analytics или Яндекс.Метрике. Нажмите на кнопку Сохранить.

Похожие статьи:

Добавить комментарий