Estatísticas do site e seu próprio pequeno armazenamento

O Webalizer e o Google Analytics me ajudaram a obter informações sobre o que está acontecendo nos sites há muitos anos. Agora entendo que eles fornecem muito pouca informação útil. Tendo acesso ao seu arquivo access.log, fica muito fácil entender as estatísticas e implementar ferramentas bastante básicas, como sqlite, html, a linguagem sql e qualquer linguagem de programação de script.

A fonte de dados do Webalizer é o arquivo access.log do servidor. É assim que se parecem suas barras e números, dos quais apenas fica claro o volume total de tráfego:

Estatísticas do site e seu próprio pequeno armazenamento
Estatísticas do site e seu próprio pequeno armazenamento
Ferramentas como o Google Analytics coletam dados da própria página carregada. Eles nos mostram alguns diagramas e linhas, com base nos quais muitas vezes é difícil tirar conclusões corretas. Talvez devesse ter sido feito mais esforço? Não sei.

Então, o que eu queria ver nas estatísticas de visitantes do site?

Tráfego de usuários e bots

Muitas vezes o tráfego do site é limitado e é necessário ver quanto tráfego útil está sendo utilizado. Por exemplo, assim:

Estatísticas do site e seu próprio pequeno armazenamento

Consulta de relatório 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

O gráfico mostra a atividade constante dos bots. Seria interessante estudar detalhadamente os representantes mais ativos.

Bots irritantes

Classificamos os bots com base nas informações do agente do usuário. Estatísticas adicionais sobre o tráfego diário e o número de solicitações bem-sucedidas e malsucedidas dão uma boa ideia da atividade do bot.

Estatísticas do site e seu próprio pequeno armazenamento

Consulta de relatório 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

Neste caso, o resultado da análise foi a decisão de restringir o acesso ao site adicionando-o ao arquivo robots.txt

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

Os dois primeiros bots desapareceram da mesa e os robôs MS desceram das primeiras linhas.

Dia e horário de maior atividade

As subidas são visíveis no trânsito. Para estudá-los detalhadamente, é necessário destacar o horário de sua ocorrência, não sendo necessário exibir todas as horas e dias de medição do tempo. Isso tornará mais fácil encontrar solicitações individuais no arquivo de log, caso seja necessária uma análise detalhada.

Estatísticas do site e seu próprio pequeno armazenamento

Consulta de relatório 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

Observamos as horas mais ativas 11, 14 e 20 do primeiro dia do gráfico. Mas no dia seguinte, às 13h, os bots estavam ativos.

Atividade média diária do usuário por semana

Resolvemos um pouco as coisas com atividade e tráfego. A próxima questão foi a atividade dos próprios usuários. Para tais estatísticas, são desejáveis ​​longos períodos de agregação, como uma semana.

Estatísticas do site e seu próprio pequeno armazenamento

Consulta de relatório 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

As estatísticas semanais mostram que, em média, um usuário abre 1,6 páginas por dia. A quantidade de arquivos solicitados por usuário neste caso depende da adição de novos arquivos ao site.

Todas as solicitações e seus status

O Webalizer sempre mostrava códigos de páginas específicos e eu sempre quis ver apenas o número de solicitações bem-sucedidas e erros.

Estatísticas do site e seu próprio pequeno armazenamento

Consulta de relatório 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

O relatório exibe solicitações, não cliques (ocorrências). Ao contrário de LINE_CNT, a métrica REQUEST_CNT é calculada como COUNT(DISTINCT STG.REQUEST_NK). O objetivo é mostrar eventos efetivos, por exemplo, os bots MS pesquisam o arquivo robots.txt centenas de vezes por dia e, neste caso, tais pesquisas serão contadas uma vez. Isso permite suavizar os saltos no gráfico.

No gráfico você pode ver muitos erros - são páginas inexistentes. O resultado da análise foi a adição de redirecionamentos de páginas remotas.

Solicitações incorretas

Para examinar as solicitações detalhadamente, você pode exibir estatísticas detalhadas.

Estatísticas do site e seu próprio pequeno armazenamento

Consulta de relatório 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

Esta lista também conterá todas as chamadas, por exemplo, uma solicitação para /wp-login.php. Ao ajustar as regras para reescrever solicitações pelo servidor, você pode ajustar a reação do servidor a tais solicitações e enviá-las para a página inicial.

Portanto, alguns relatórios simples baseados no arquivo de log do servidor fornecem uma visão bastante completa do que está acontecendo no site.

Como obter informações?

Um banco de dados sqlite é suficiente. Vamos criar tabelas: auxiliares para registro de processos ETL.

Estatísticas do site e seu próprio pequeno armazenamento

Estágio de tabela onde escreveremos arquivos de log usando PHP. Duas tabelas agregadas. Vamos criar uma tabela diária com estatísticas sobre agentes de usuários e status de solicitações. De hora em hora com estatísticas sobre solicitações, grupos de status e agentes. Quatro tabelas de medições relevantes.

O resultado é o seguinte modelo relacional:

Modelo de dadosEstatísticas do site e seu próprio pequeno armazenamento

Script para criar um objeto em um banco de dados sqlite:

Criação de objeto 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);

Estágio

No caso do arquivo access.log, é necessário ler, analisar e gravar todas as solicitações no banco de dados. Isso pode ser feito diretamente usando uma linguagem de script ou usando ferramentas sqlite.

Formato do arquivo de registro:

//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-]+) "(.*)" "(.*)"$/';

Propagação de chave

Quando os dados brutos estão no banco de dados, você precisa gravar as chaves que não estão lá nas tabelas de medição. Então será possível construir uma referência para as medições. Por exemplo, na tabela DIM_REFERRER, a chave é uma combinação de três campos.

Consulta de propagação de chave 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

A propagação para a tabela de agente do usuário pode conter lógica de bot, por exemplo, o snippet 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

Tabelas agregadas

Por último, carregaremos as tabelas agregadas; por exemplo, a tabela diária pode ser carregada da seguinte forma:

Consulta SQL para carregar agregado

/* 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

O banco de dados sqlite permite escrever consultas complexas. COM contém a preparação de dados e chaves. A consulta principal coleta todas as referências às dimensões.

A condição não permitirá carregar novamente o histórico: CAST(STG.EVENT_DT AS INTEGER) > $param_epoch_from, onde o parâmetro é o resultado da solicitação
'SELECIONE COALESCE(MAX(EVENT_DT), '3600') AS LAST_EVENT_EPOCH FROM FCT_ACCESS_USER_AGENT_DD'

A condição carregará apenas o dia inteiro: CAST(STG.EVENT_DT AS INTEGER) < strftime('%s', date('now', 'start of day'))

A contagem de páginas ou arquivos é feita de forma primitiva, por meio da busca por um ponto.

Relatórios

Em sistemas de visualização complexos, é possível criar um metamodelo baseado em objetos de banco de dados, gerenciar dinamicamente filtros e regras de agregação. Em última análise, todas as ferramentas decentes geram uma consulta SQL.

Neste exemplo, criaremos consultas SQL prontas e as salvaremos como visualizações no banco de dados - são relatórios.

Visualização

Blefe: Lindos gráficos em JavaScript foram usados ​​como ferramenta de visualização

Para isso foi necessário percorrer todos os relatórios em PHP e gerar um arquivo html com tabelas.

$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'
);

A ferramenta simplesmente visualiza tabelas de resultados.

Jogar aviator online grátis: hack aviator funciona

Usando a análise da web como exemplo, o artigo descreve os mecanismos necessários para construir data warehouses. Como pode ser visto nos resultados, as ferramentas mais simples são suficientes para análise profunda e visualização dos dados.

No futuro, usando este repositório como exemplo, tentaremos implementar estruturas como dimensões de mudança lenta, metadados, níveis de agregação e integração de dados de diferentes fontes.

Além disso, vamos dar uma olhada mais de perto na ferramenta mais simples para gerenciar processos ETL com base em uma única tabela.

Voltemos ao tópico de medir a qualidade dos dados e automatizar esse processo.

Estudaremos os problemas do ambiente técnico e manutenção dos armazenamentos de dados, para os quais implementaremos um servidor de armazenamento com recursos mínimos, por exemplo, baseado em Raspberry Pi.

Fonte: habr.com

Adicionar um comentário