Statisticile site-ului și propriul mic depozit

Utilitarul Webalizer și instrumentul Google Analytics m-au ajutat mulți ani să înțeleg ceea ce se întâmplă pe site-uri. Acum înțeleg că oferă foarte puține informații utile. Având acces la fișierul meu access.log, este foarte simplu să analizez statisticile, folosind instrumente elementare precum sqlite, html, limbajul sql și orice limbaj de programare de scriptare.

Sursa de date pentru Webalizer este fișierul access.log server. Așa arată coloanele și cifrele, din care se înțelege doar volumul total de trafic:

Statisticile site-ului și propriul mic depozit
Statisticile site-ului și propriul mic depozit
Instrumente precum Google Analytics colectează date de pe pagina încărcată în mod autonom. Ne afișează câteva grafice și linii, pe baza cărora adesea este greu să tragem concluzii corecte. Poate că ar fi trebuit să depunem mai mult efort? Nu știu.

Deci, ce mi-ar fi plăcut să văd în statisticile de vizitare a site-ului?

Traficul utilizatorilor și al botilor

Adesea, traficul site-urilor are limitări și este necesar să vedem cât din traficul util este utilizat. De exemplu, așa:

Statisticile site-ului și propriul mic depozit

Interogarea SQL a raportului

SELECT
1 AS 'StackedArea: Trafic generat de utilizatori și botii',
strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Zi',
SUM(CASE WHEN USG.AGENT_BOT!='n.a.' THEN FCT.BYTES ELSE 0 END)/1000 AS 'Boti, KB',
SUM(CASE WHEN USG.AGENT_BOT='n.a.' THEN FCT.BYTES ELSE 0 END)/1000 AS 'Utilizatori, 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

Din grafic se vede activitatea constantă a botilor. Ar fi interesant să studiem mai în detaliu cei mai activi reprezentanți.

Boti enervanți

Clasificăm botii pe baza informațiilor despre agentul utilizator. Statistici suplimentare despre traficul zilnic, numărul de cereri reușite și nereușite oferă o imagine bună asupra activității botilor.

Statisticile site-ului și propriul mic depozit

Interogarea SQL a raportului

SELECT 
1 AS 'Tabel: Boti enervanți',
MAX(USG.AGENT_BOT) AS 'Bot',
ROUND(SUM(FCT.BYTES)/1000/14.0, 1) AS 'KB pe zi',
ROUND(SUM(FCT.IP_CNT)/14.0, 1) AS 'IPs pe zi',
ROUND(SUM(CASE WHEN STS.STATUS_GROUP IN ('Client Error', 'Server Error') THEN FCT.REQUEST_CNT/14.0 ELSE 0 END), 1) AS 'Cereri de eroare pe zi',
ROUND(SUM(CASE WHEN STS.STATUS_GROUP IN ('Successful', 'Redirection') THEN FCT.REQUEST_CNT/14.0 ELSE 0 END), 1) AS 'Cereri reușite pe zi',
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

În acest caz, analiza a dus la decizia de a restricționa accesul la site prin adăugarea în fișierul robots.txt

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

Primele două boturi au dispărut din tabel, iar roboții MS s-au mutat de pe primele locuri în jos.

Ziua și ora cu cea mai mare activitate

În trafic se observă creșteri. Pentru a le analiza în detaliu, este necesar să se evidențieze perioada apariției acestora, fiindcă nu este obligatoriu să se afișeze toate orele și zilele de măsurare a timpului. Astfel, va fi mai ușor să găsiți anumite cereri în fișierul de log în caz de analiză detaliată.

Statisticile site-ului și propriul mic depozit

Interogarea SQL a raportului

SELECT
1 AS 'Linie: Ziua și Ora Accesărilor din Utilizatori și Roboți',
strftime('%d.%m-%H', datetime(EVENT_DT, 'unixepoch')) AS 'Data și Ora',
HIB AS 'Roboți, Accesări',
HIU AS 'Utilizatori, Accesări'
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

Observăm cele mai active ore 11, 14 și 20 în prima zi pe grafic. Iar a doua zi, în ora 13 au activat roboții.

Activitatea medie zilnică a utilizatorilor pe săptămâni

Ne-am lămurit puțin cu activitatea și traficul. Următoarea întrebare a fost activitatea utilizatorilor înșiși. Pentru o astfel de statistică, sunt dorite perioade de agregare mari, de exemplu, o săptămână.

Statisticile site-ului și propriul mic depozit

Interogarea SQL a raportului

SELECT
1 AS 'Linie: Activitate Medie Zilnică a Utilizatorilor pe Săptămână',
strftime('%W week', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Săptămâna',
ROUND(1.0*SUM(FCT.PAGE_CNT)/SUM(FCT.IP_CNT),1) AS 'Pagini per IP pe Zi',
ROUND(1.0*SUM(FCT.FILE_CNT)/SUM(FCT.IP_CNT),1) AS 'Fișiere per IP pe Zi'
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.' /* utilizatori doar */
  AND HST.STATUS_GROUP IN ('Successful') /* pagini bune */
  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

Statistica pe săptămână arată că, în medie, un utilizator deschide 1,6 pagini pe zi. Numărul de fișiere solicitate pe utilizator în acest caz depinde de adăugarea de fișiere noi pe site.

Toate cererile și statusurile acestora

Webalizer a arătat întotdeauna codurile specifice ale paginilor și mereu a fost dorit să se vadă pur și simplu numărul cererilor reușite și al erorilor.

Statisticile site-ului și propriul mic depozit

Interogarea SQL a raportului

SELECT
1 as 'Linie: Toate cererile după statut',
strftime('%d.%m', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Zi',
SUM(CASE WHEN STS.STATUS_GROUP='Successful' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Suces',
SUM(CASE WHEN STS.STATUS_GROUP='Redirection' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Redirecționare',
SUM(CASE WHEN STS.STATUS_GROUP='Client Error' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Eroare Client',
SUM(CASE WHEN STS.STATUS_GROUP='Server Error' THEN FCT.REQUEST_CNT ELSE 0 END) AS 'Eroare Server'
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

Raportul arată cererile, nu clicurile (hituri); spre deosebire de metrica LINE_CNT, REQUEST_CNT este considerat ca COUNT(DISTINCT STG.REQUEST_NK). Scopul este de a arăta evenimentele eficiente, de exemplu, boturile MS interoghează de sute de ori pe zi fișierul robots.txt și, în acest caz, aceste interogări vor fi contorizate o singură dată. Aceasta ajută la netezirea vârfurilor din grafic.

Din grafic se pot observa multe erori — pagini inexistente. Ca rezultat al analizei, s-au adăugat redirecționări pentru paginile șterse.

Cererile eronate

Pentru o analiză detaliată a cererilor, se poate genera o statistică detaliată.

Statisticile site-ului și propriul mic depozit

Interogarea SQL a raportului

SELECT
  1 AS 'Tabel: Cele mai frecvente cereri eronate',
  REQ.REQUEST_NK AS 'Cereră',
  'Eroare' AS 'Starea Cererii',
  ROUND(SUM(FCT.LINE_CNT) / 14.0, 1) AS 'Hituri pe zi',
  ROUND(SUM(FCT.IP_CNT) / 14.0, 1) AS 'IP-uri pe zi',
  ROUND(SUM(FCT.BYTES)/1000 / 14.0, 1) AS 'KB pe zi'
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

În această listă vor fi incluse și toate apelurile, de exemplu, cererea către /wp-login.php. Prin ajustarea regulilor de rescriere a cererilor serverul se poate corecta reacția serverului la astfel de cereri și să le redirecționeze către pagina de start.

Astfel, câteva rapoarte simple bazate pe fișierul jurnal al serverului oferă o imagine destul de completă a ceea ce se întâmplă pe site.

Cum obținem informații?

Baze de date sqlite sunt mai mult decât suficiente. Vom crea tabele: o tabelă auxiliară pentru jurnalizarea proceselor ETL.

Statisticile site-ului și propriul mic depozit

Tabelă de etaj, unde vom scrie fișierele de jurnal cu ajutorul PHP. Două tabele de agregate. Vom crea o tabelă zilnică cu statisticile pe agenții utilizatori și stările cererilor. Una orară cu statisticile cererilor, grupurilor de stări și agenților. Patru tabele de dimensiuni corespunzătoare.

În rezultat, a ieșit următoarea modelare relațională:

Modelul de dateStatisticile site-ului și propriul mic depozit

Script pentru creare obiect în baza de date sqlite:

DDL creare obiect

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

Stadiu

În cazul fișierului access.log, trebuie citite, analizate și înregistrate în bază toate cererile. Acest lucru se poate face fie direct prin intermediul unui limbaj de scriptare, fie utilizând instrumentele sqlite.

Formatul fișierului de log:

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

Propagarea cheilor

Când datele brute sunt în bază, trebuie să înregistrăm în tabelele de dimensiuni cheile care nu există acolo. Apoi va fi posibil să construim un link pe dimensiuni. De exemplu, în tabelul DIM_REFERRER, cheia este combinația a trei câmpuri.

Interogarea SQL pentru propagarea cheilor

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

Propagarea în tabelul agenților de utilizator poate conține logica roboților, de exemplu, un fragment 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 'altul'
ELSE 'n.a.' END AS AGENT_BOT

Tabelele agregatelor

În ultimul rând, vom încărca tabelele agregatelor, de exemplu, tabelul zilnic poate fi încărcat în felul următor:

Interogarea SQL pentru încărcarea agregatului

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

Baza de date sqlite permite scrierea de interogări complexe. WITH conține pregătirea datelor și cheilor. Interogarea principală adună toate linkurile pe dimensiuni.

Condiția nu va permite încărcarea din nou a istoricului: CAST(STG.EVENT_DT AS INTEGER) > $param_epoch_from, unde parametrul este rezultatul interogării
‘SELECT COALESCE(MAX(EVENT_DT), ‘3600’) AS LAST_EVENT_EPOCH FROM FCT_ACCESS_USER_AGENT_DD’

Condiția va încărca doar ziua întreagă: CAST(STG.EVENT_DT AS INTEGER) < strftime(‘%s’, date(‘now’, ‘start of day’))

Numărarea paginilor sau fișierelor se efectuează într-un mod simplu, prin căutarea punctului.

Rapoarte

În sistemele complexe de vizualizare există posibilitatea de a crea o meta-model pe baza obiectelor din baza de date, gestionând dinamic filtrele și regulile de agregare. În cele din urmă, toate instrumentele decente generează interogări SQL.

În acest exemplu, vom crea interogări SQL deja pregătite și le vom salva ca vizualizări în baza de date — acestea sunt rapoartele.

Vizualizare

Ca instrument de vizualizare a fost folosit Bluff: grafice frumoase în JavaScript.

Pentru aceasta, a fost necesar să parcurgem toate rapoartele cu ajutorul PHP și să generăm un fișier HTML cu tabele.

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

Instrumentul vizualizează pur și simplu tabelele de rezultate.

Ieșire

Prin exemplul analizei web, articolul descrie mecanismele necesare pentru construirea de depozite de date. Așa cum se poate observa din rezultate, pentru o analiză profundă și vizualizarea datelor sunt suficiente cele mai simple instrumente.

În continuare, vom încerca să implementăm structuri precum dimensiuni care se schimbă lent, metadate, niveluri de agregare și integrarea datelor din diferite surse, folosind acest depozit ca exemplu.

De asemenea, vom examina mai în detaliu un instrument simplu de gestionare a proceselor ETL pe baza unei singure tabele.

Să revenim la subiectul măsurării calității datelor și automatizării acestui proces.

Vom studia problemele mediului tehnic și întreținerea depozitelor de date, pentru care vom implementa un server de depozit cu resurse minime, de exemplu, pe baza Raspberry Pi.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster