Sistema di statistiche per siti e blog
Vediamo come fare a creare in modo facile e veloce un sistema di statistiche per monitorare gli accessi di un sito web. Codice ed esempio.
Introduzione
Realizzeremo un sistema di statistiche per monitore gli accessi in un sito web. Un sistema di statistiche dettagliato non permette solo il conteggio degli accessi ad un sito, ma permette la creazione di un completo report che una volta analizzato potrà fornirci utili informazioni sulle abitudine degli utenti. Studieremo quindi WMPStat, il sistema di statistiche OpenSource (codice libero) realizzato dalla community di WebMasterPoint.org, non è ancora una versione perfetta al 100%, ma ottima per lo studio.
Il progetto
Un sistema di statistiche può essere realizzato in vari modi ma le caratteristiche principali che deve avere è ottenere molti dati importanti e realistici nel minor tempo possibile per non rallentare il caricamento della pagina da monitorare. Il software è basato su due pagine principali e un database nel nostro caso Access che permetterà di memorizzare le informazioni raccolte.
Raccolta dei dati
La pagina che raccoglie i dati (lez11_entry.asp) è il cuore dello script e deve essere realizzata nel modo piu' performante possibile, in quanto, una sua lenta esecuzione comporterebbe ad un caricamento della pagina web richiesta piu' lento.
La raccolta dei dati è molto complessa, quindi il codice sarà analizzato a blocchi, in ogni caso è tutto spiegato anche nei commenti.
<%'On Error Resume Next'*****************************************************************' apro la connessione%><!-- #include file="connessione.asp" --><%'*****************************************************************' dichiaro tutte le variabilidim ip, ipOnline, giorno_settimana, data, ora, browser, browserUtilizzato, numeroBrowserdim giorno, mese, anno, ore, minuti, secondidim dataGiorni, dataGiorniOggi, impressionGiorni, visitatoriGiornidim ingresso, provenienzaIngresso, urlIngresso, numeroIngresso, provenienza, url, arrUrlIngressodim oraMedia, accessiMedia, impressionMediadim nomeMediaSett, accessiMediaSett, impressionMediaSettdim pagineViste, arrPagineViste, urlPagineViste, numeroPagineVistedim urlProvenienza, numeroProvenienza, dataProvenienza, data_ultimoProvenienzadim settimana_anno, settimanaAnno, settimanaSettimana, settimanaAccessi, settimanaImpressiondim so, numeroSo, soSodim linkMotore, key, keyMotore, keyStr'*****************************************************************data = Date()ora = Time()settimana_anno = DatePart("ww", data)
Il comando che disabilita l'interruzione dell'esecuzione della pagina al verificarsi di un errore (On Errore Resume Next) è commentato, ma per esser sicuri che lo script non causi il blocco della pagina per un eventuale errore (per esempio l'impossibilità di connettersi al database) è consigliato eliminare il commento. Viene poi inclusa la pagina connessione.asp che ha il compito di aprire la connessione al database, dichiarate tutte le variabili utilizzate e infine memorizzati la data, l'anno e il numero della settimana nell'anno importanti per raccogliere i dati
' ricavo il giorno della settimana di oggigiorno_settimana = Weekday(data)Select Case giorno_settimanaCase "1"giorno_settimana = "Domenica"Case "2"giorno_settimana = "Lunedì"Case "3"giorno_settimana = "Martedì"Case "4"giorno_settimana = "Mercoledi"Case "5"giorno_settimana = "Giovedì"Case "6"giorno_settimana = "Venerdì"Case "7"giorno_settimana = "Sabato"End Select
Viene ricavato con la funzione Weekday() quale giorno della settimana è al momento della richiesta di questa pagina e con un Select Case (costrutto simile a If... Then... Else, ma molto piu' comodo per la verifica di molte condizioni) viene memorizzata nella variabile giorno_settimana il nome del giorno.
' separo la data di oggigiorno = Day(data)mese = Month(data)anno = Year(data)if mese < 10 thenmese = "0" & meseend ifif giorno < 10 thengiorno = "0" & giornoend if'*****************************************************************' separo l'ora odiernaore = Hour(ora)minuti = Minute(ora)secondi = Second(ora)ora = ore & ":" & minuti & ":" & secondi'*****************************************************************' mi ricavo l'ip del visitatoreip = Request.ServerVariables("REMOTE_ADDR")
E' inoltre necessario ai fini della statistica memorizzare i valori del giorno, mese, anno, ora, minuti e secondi... nonchè dell' indirizzo IP dell'utente per poter conteggiare anche gli accessi unici. Qui si conclude la fase di "preparazione".
' controllo che non sia già esistente entro un oraSQL = "SELECT * FROM ip WHERE ip='" & ip & "' AND ora=" & oreSet rs = Server.CreateObject("ADODB.Recordset")rs.Open SQL, objConn, 3, 3'*****************************************************************' se l'IP non esiste nell'ultima ora entro nel ifif rs.EOF then' lo inserisco nella tabella ipSQL = "INSERT INTO ip(ip, ora) VALUES ('" & ip & "', " & ore & ")"Set rs1 = Server.CreateObject("ADODB.Recordset")rs1.Open SQL, objConn, 3, 3elseipOnline = rs.Fields("ip")end if
Per prima cosa viene controllato se l' IP dell'utente è già presente nel database (il sistema memorizza gli IP dell'ultima ora), se non è esistente lo inserisce.
' controllo che nella tabella giorni ci siano già recordSQL = "SELECT * FROM giorni WHERE data=" & anno & "" & mese & "" & giornoSet rs4 = Server.CreateObject("ADODB.Recordset")rs4.Open SQL, objConn, 3, 3if not rs4.EOF thendataGiorni = rs4.Fields("data")impressionGiorni = rs4.Fields("impression")visitatoriGiorni = rs4.Fields("visitatori")end if
Ora è necessario controllare che nella tabella giorni del database sia già stato inserito il giorno attuale, in tal caso vengono prelevate le informazioni sulle impression e i visitatori del giorno che poi dovranno essere aggiornate.
dataGiorniOggi = anno & "" & mese & "" & giorno' se ci sono record modifico il campo impression giornaliere altrimenti inserisco un recordif (int(dataGiorni) = int(dataGiorniOggi)) thenSQL = "UPDATE giorni SET impression=" & int(impressionGiorni)+1 & " WHERE data=" & datagiorniOggiif ipOnline = "" then'*****************************************************************' nella tabella giorni modifico il campo come modifica univocaSQL = "UPDATE giorni SET visitatori=" & int(visitatoriGiorni)+1 & " WHERE data=" & int(dataGiorniOggi)Set rs6 = Server.CreateObject("ADODB.Recordset")rs6.Open SQL, objConn, 3, 3end ifelseSQL = "INSERT INTO giorni(data, impression, visitatori)" _& " VALUES(" & dataGiorniOggi & ", 1, 1)"end ifSet rs5 = Server.CreateObject("ADODB.Recordset")rs5.Open SQL, objConn, 3, 3
Vengono modificati i dati ricavati dalla tabella giorni, le impressions vengono aumentate di + 1, i visitatori, se poi è il primo accesso dell'utente che viene monitorato anche il campo visitatori viene aggiornato di + 1
provenienza = Request.ServerVariables("HTTP_REFERER") if provenienza <> "" then '***************************************************************** ' controllo la provenienza dell'utente e i tempi SQL = "SELECT * FROM provenienza WHERE url='" & provenienza & "'" Set rs17 = Server.CreateObject("ADODB.Recordset") rs17.Open SQL, objConn, 3, 3 if not rs17.EOF then urlProvenienza = rs17.fields("url") numeroProvenienza = rs17.fields("numero") dataProvenienza = rs17.fields("data") data_ultimoProvenienza = rs17.fields("data_ultimo") end if if (ipOnline = ip) then 'non si fa niente else if(Trim(LCase(urlProvenienza)) = Trim(LCase(provenienza))) then if ipOnline = "" then SQL = "UPDATE provenienza SET numero=" & int(numeroProvenienza)+1 & ", data_ultimo=#" & mese &"/"& giorno &"/"& anno & "# WHERE url='" & urlProvenienza & "'" end if elseif (ipOnline <> ip) then SQL = "INSERT INTO provenienza(url, numero, data, data_ultimo)" _ & " VALUES('" & provenienza & "', 1, #" & mese &"/"& giorno &"/"& anno & "#, #" & mese &"/"& giorno &"/"& anno & "#)" end if Set rs18 = Server.CreateObject("ADODB.Recordset") rs18.Open SQL, objConn, 3, 3 end if end if ' controllo da dove proviene l'utente ingresso = Request.Servervariables("PATH_INFO") SQL = "SELECT * FROM ingresso WHERE url='" & Trim(ingresso) & "'" Set rs7 = Server.CreateObject("ADODB.Recordset") rs7.Open SQL, objConn, 3, 3 if not rs7.EOF then urlIngresso = rs7.fields("url") numeroIngresso = rs7.fields("numero") provenienzaIngresso = rs7.fields("provenienza") end if if (ipOnline = ip) then 'non si fa niente else if Trim(LCase(ingresso)) = Trim(LCase(urlIngresso)) then SQL = "UPDATE ingresso SET numero=" & int(numeroIngresso)+1 & " WHERE url='" & urlIngresso & "'" elseif (ipOnline <> ip) then arrUrlIngresso = Split(ingresso, "/", -1, 1) url = arrUrlIngresso(1) SQL = "INSERT INTO ingresso(url, numero, provenienza)" _ & " VALUES('" & Trim(LCase(ingresso)) & "', 1, '" & url & "')" end if Set rs8 = Server.CreateObject("ADODB.Recordset") rs8.Open SQL, objConn, 3, 3 end if
Viene ora controllata la provenienza con l'utilizzo di Request.ServerVariables("HTTP_REFERER") se non contiene niente le operazioni successive non saranno effettuate (a volte può capitare che non si riesca a prelevare l'url della pagina da cui l'utente proviene, i motivi posso essere svariati: può provenire da un link presente in una email per esempio). Se è possibile intercettare il Referer (provenienza) allora viene controllato se la pagina da cui proviene è già inserita nel database, se lo è allora aggiorna il campo numero di + 1 per indicare che un ulteriore utente proviene da quella pagina, se non è presente l'url, viene inserita con il campo numero impostato a 1, in quanto è il primo accesso proveniente da quella pagina.
' controllo le pagine visitatepagineViste = Request.Servervariables("PATH_INFO")arrPagineViste = Split(pagineViste, "/", -1, 1)pagineViste = arrPagineViste(UBound(arrPagineViste)-1) & "/" & arrPagineViste(UBound(arrPagineViste))SQL = "SELECT * FROM pagineviste WHERE url='" & pagineViste & "'"Set rs15 = Server.CreateObject("ADODB.Recordset")rs15.Open SQL, objConn, 3, 3if not rs15.EOF thenurlPagineViste = rs15.fields("url")numeroPagineViste = rs15.fields("numero")end ifif (Trim(Lcase(urlPagineViste)) = Trim(Lcase(pagineViste))) thenSQL = "UPDATE pagineviste SET numero=" & int(numeroPagineViste)+1 & " WHERE url='" & urlPagineViste & "'"elseSQL = "INSERT INTO pagineviste(url, numero)" _& " VALUES('" & pagineViste & "', 1)"end ifSet rs16 = Server.CreateObject("ADODB.Recordset")rs16.Open SQL, objConn, 3, 3
Viene prelevata la pagina che sta visitando l'utente e viene effettuato lo stesso ragionamento per i Referer. Se la pagina è già presente nel database il campo numero viene aggiornato di + 1, altrimenti l'url della pagina visualizzata viene inserito e il campo numero impostato a 1.
' controllo per settimana dell'annoSQL = "SELECT * FROM settimane WHERE anno=" & anno & " AND settimana=" & settimana_annoSet rs19 = Server.CreateObject("ADODB.Recordset")rs19.Open SQL, objConn, 3, 3if not rs19.EOF thensettimanaAnno = rs19.Fields("anno")settimanaSettimana = rs19.Fields("settimana")settimanaAccessi = rs19.Fields("accessi")settimanaImpression = rs19.Fields("impression")end ifif (int(settimanaSettimana) = int(settimana_anno)) thenSQL = "UPDATE settimane SET impression=" & int(settimanaImpression)+1 & " WHERE anno=" & anno & " AND settimana=" & settimanaSettimanaif ipOnline = "" thenSQL = "UPDATE settimane SET accessi=" & int(settimanaAccessi)+1 & " WHERE anno=" & anno & " AND settimana=" & settimana_annoend ifelseSQL = "INSERT INTO settimane(anno, settimana, accessi, impression)" _& " VALUES(" & anno & ", " & settimana_anno & ", 1, 1)"end ifSet rs20 = Server.CreateObject("ADODB.Recordset")rs20.Open SQL, objConn, 3, 3'*****************************************************************' inserisco la media oreSQL = "SELECT * FROM media_ore WHERE ora=" & oreSet rs9 = Server.CreateObject("ADODB.Recordset")rs9.Open SQL, objConn, 3, 3if not rs9.EOF thenoraMedia = rs9.Fields("ora")accessiMedia = rs9.Fields("accessi")impressionMedia = rs9.Fields("impression")end ifif (int(oraMedia) = int(ore)) thenSQL = "UPDATE media_ore SET impression=" & int(impressionMedia)+1 & " WHERE ora=" & oreif ipOnline = "" thenSQL = "UPDATE media_ore SET accessi=" & int(accessiMedia)+1 & " WHERE ora=" & oreend ifelseSQL = "INSERT INTO media_ore(ora, accessi, impression)" _& " VALUES(" & ore & ", 1, 1)"end ifSet rs10 = Server.CreateObject("ADODB.Recordset")rs10.Open SQL, objConn, 3, 3'*****************************************************************' inserisco la media settimanaleSQL = "SELECT * FROM media_settimana WHERE nome='" & giorno_settimana & "'"Set rs12 = Server.CreateObject("ADODB.Recordset")rs12.Open SQL, objConn, 3, 3if not rs12.EOF thennomeMediaSett = rs12.Fields("nome")accessiMediaSett = rs12.Fields("accessi")impressionMediaSett = rs12.Fields("impression")end ifif (Trim(LCase(nomeMediaSett)) = Trim(LCase(giorno_settimana))) thenSQL = "UPDATE media_settimana SET impression=" & int(impressionMediaSett)+1 & " WHERE nome='" & nomeMediaSett & "'"if ipOnline = "" thenSQL = "UPDATE media_settimana SET accessi=" & int(accessiMediaSett)+1 & " WHERE nome='" & giorno_settimana & "'"end ifelseSQL = "INSERT INTO media_settimana(nome, accessi, impression)" _& " VALUES('" & giorno_settimana & "', 1, 1)"end ifSet rs13 = Server.CreateObject("ADODB.Recordset")rs13.Open SQL, objConn, 3, 3
Con tutto questo codice vengono effettuate 3 operazioni differenti, ma volte tutte a raccogliere dati sugli accessi in base a determinati periodi di tempo:
- settimana dell'anno (accessi nella 1°, 2°, 3°... 50° settimana dell'anno)
- media ore (media accessi alle 01, 02, 03... 19... 22, 23)
- media settimanale (media accessi il lunedì, martedì, mercoledì... )
Viene prima controllato se il record è presente, vengono recuperate le informazioni sugli accessi (accessi e impression) e poi aggiornati o inseriti nel caso non ci sia il record.
' mi ricavo il browser dell'utente e poi lo inserisco nel databasebrowser = UCase(Request.ServerVariables("HTTP_USER_AGENT"))if InStr(browser, "MSIE") thenbrowser = "Internet Explorer"elseif InStr(browser, "OPERA") thenbrowser = "Opera"elsebrowser = "Netscape"end ifSQL = "SELECT * FROM browser WHERE browser='" & browser & "'"Set rs2 = Server.CreateObject("ADODB.Recordset")rs2.Open SQL, objConn, 3, 3if rs2.EOF thenSQL = "INSERT INTO browser(browser, numero) VALUES('" & browser & "', 1)"elsebrowserUtilizzato = rs2.fields("browser")numeroBrowser = rs2.fields("numero")if ((ipOnline = ip) AND (browserUtilizzato = browser)) then'non si fa nienteelseSQL = "UPDATE browser SET numero=" & int(numeroBrowser)+1 & " WHERE browser='" & browser & "'"end ifend ifSet rs3 = Server.CreateObject("ADODB.Recordset")rs3.Open SQL, objConn, 3, 3'*****************************************************************' mi ricavo il sistema operativo dell'utente e poi lo inserisco nel databaseso = UCase(Request.ServerVariables("HTTP_USER_AGENT"))if instr(so, "98") thenso = "Windows 98"elseif instr(so, "95") thenso = "Windows 95"elseif instr(so, "NT 5.0") thenso = "Windows2K"elseif instr(so, "NT 5.1") thenso = "Windows Xp"elseif instr(so, "NT") thenso = "Windows NT"elseif instr(so, "LINUX") thenso = "Linux"elseif instr(so, "MAC") thenso = "Machintos"end ifSQL = "SELECT * FROM sistema WHERE os='" & so & "'"Set rs22 = Server.CreateObject("ADODB.Recordset")rs22.Open SQL, objConn, 3, 3if rs22.EOF thenSQL = "INSERT INTO sistema(os, numero) VALUES('" & so & "', 1)"elsesoSo = rs22.Fields("os")numeroSo = rs22.Fields("numero")if ((ipOnline = ip) AND (soSo = so)) then'non si fa nienteelseSQL = "UPDATE sistema SET numero=" & int(numeroSo)+1 & " WHERE os='" & soSo & "'"end ifend ifSet rs23 = Server.CreateObject("ADODB.Recordset")rs23.Open SQL, objConn, 3, 3
Il codice ricava le informazioni sul browser e il sistema operativo dell'utente per poi memorizzarli e capire i nostri utenti che software utilizzano. Questi dati sono normalmente simili in molti siti web, ma in siti di grafica professionale o orientati ad un particolare sistema operativo noteremo differenze... in quelli di grafica ci sarà una maggiore percentuale rispetto agli altri di Mac per esempio. Queste informazioni possono apparire inutile, ma al contrario (come abbiamo già analizzato su WebMasterPoint.org in un articolo di webmarketing) sono utili per comprendere che tipo di utenti accedono al nostro sito. Il ragionamento per l'inserimento delle informazioni è identico a quello visto in precedenza.
' mi ricavo il motore di ricerca e la keywords 'Controllo Google If Instr(provenienza,"google.it") then arrKey = Split(provenienza, "q=") key = Replace(provenienza,"http://www.google.it/search?q="," ") key = key & "" & arrKey(1) keyMotore = key linkMotore = "http://www.google.it" 'Controllo Virgilio.it elseIf Instr(provenienza,"virgilio.it") then arrKey = Split(provenienza, "qs=") key = Replace(provenienza,"http://search.virgilio.it/search/cgi/search.cgi?db=v&op=and&qs=","") key = key & "" & arrKey(1) keyMotore = key linkMotore = "http://www.virgilio.it" 'Controllo Google.com elseIf Instr(provenienza,"google.com") then arrKey = Split(provenienza, "q=") key = Replace(provenienza,"http://www.google.com/search?hl=en&q="," ") key = key & "" & arrKey(1) keyMotore = key linkMotore = "http://www.google.com" 'Controllo Altavista.com elseIf Instr(provenienza,"www.altavista.com") then arrKey = Split(provenienza, "qs") key = Replace(provenienza,"http://www.altavista.com/sites/search/web?q="," ") key = key & "" & arrKey(1) keyMotore = key linkMotore = "http://www.altavista.com" 'Controllo Altavista.it elseIf Instr(provenienza,"it.altavista.com") then arrKey = Split(provenienza, "q=") key = Replace(provenienza,"http://it.altavista.com/q?pg=q&q="," ") key = key & "" & arrKey(1) keyMotore = key linkMotore = "http://www.altavista.it" 'Controllo Lycos.it elseIf Instr(provenienza,"cerca.lycos.it") then arrKey = Split(provenienza, "query=") key = Replace(provenienza,"http://cerca.lycos.it/cgi-bin/pursuit?matchmode=and&mtemp=main&etemp=error&query="," ") key = key & "" & arrKey(1) keyMotore = key linkMotore = "http://www.lycos.it" 'Controllo Yahoo.it elseIf Instr(provenienza,"it.search.yahoo.com") then arrKey = Split(provenienza, "p=") key = Replace(provenienza,"http://it.search.yahoo.com/search/it?p="," ") key = key & "" & arrKey(1) keyMotore = key linkMotore = "http://www.yahoo.it" 'Controllo Yahoo.com elseIf Instr(provenienza,"search.yahoo.com") then arrKey = Split(provenienza, "p=") key = Replace(provenienza,"http://search.yahoo.com/bin/search?p="," ") key = key & "" & arrKey(1) keyMotore = key linkMotore = "http://www.yahoo.com" End If ' inserisce nel database il motore If linkMotore <> "" then SQL = "SELECT * FROM motore WHERE motore='" & linkMotore & "'" Set rs24 = Server.CreateObject("ADODB.Recordset") rs24.Open SQL, objConn, 3, 3 if rs24.EOF then SQL = "INSERT INTO motore(motore, accessi) VALUES('" & linkMotore & "', 1)" else SQL = "UPDATE motore SET accessi=" & int(accessi)+1 & " WHERE motore='" & linkMotore & "'" end if Set rs25 = Server.CreateObject("ADODB.Recordset") rs25.Open SQL, objConn, 3, 3 '***************************************************************** ' inserisce nel database la keyword SQL = "SELECT * FROM keyword WHERE keyword='" & keyMotore & "'" Set rs26 = Server.CreateObject("ADODB.Recordset") rs26.Open SQL, objConn, 3, 3 if rs26.EOF then SQL = "INSERT INTO keyword(keyword, numero) VALUES('" & keyMotore & "', 1)" else SQL = "UPDATE keyword SET numero=" & int(numero)+1 & " WHERE keyword='" & keyMotore & "'" end if Set rs27 = Server.CreateObject("ADODB.Recordset") rs27.Open SQL, objConn, 3, 3 End If
Questo codice permette di ricavare gli accessi provenienti da eventuali motori di ricerca e le parole chiave utilizzate. Questo codice contiene ancora un bug da correggere ma è utile analizzare le funzioni utilizzate:
'Controllo GoogleIf Instr(provenienza,"google.it") thenarrKey = Split(provenienza, "q=")key = Replace(provenienza,"http://www.google.it/search?q="," ")key = key & "" & arrKey(1)keyMotore = keylinkMotore = "http://www.google.it"'Controllo Virgilio.itelseIf Instr(provenienza,"virgilio.it") then....
La funzione Instr(var,testo) permette di verificare se all'interno di var ci sia testo, la funzione restituisce True o False.
Se l'url è: http://www.google.it/search?sourceid=navclient&hl=it&ie=UTF-8&oe=UTF-8&q=cerca del motore google (a volte i parametri sono messi in ordine differente), l'istruzione:
Instr(provenienza,"google.it")
restituire True come valore.
La funzione Split(var,testo) permette di dividere una stringa var in un array dividendola prendendo come separatore testo.
Con la stessa url di prima l'istruzione:
arrKey = Split(provenienza, "q=")
crea un array con 2 record che contengono:
- http://www.google.it/search?sourceid=navclient&hl=it&ie=UTF-8&oe=UTF-8&
- q=cerca
In questo caso dovremmo prendere il secondo record che contiene "q=cerca" e fare un Replace(arrKey(1),"p=","") per eliminare q= e ottenere la parola chiave. Questa parola viene quindi memorizzata nella variabile key, viene impostato di quale motore si tratta in linkmotore. Infine key e linkmotore saranno inseriti nel database.
Visualizzazione delle statistiche
La pagina che si occupa della visualizzazione delle statistiche (statistiche.asp) pur essendo lunga centinaia di righe di codice (oltre 700) è molto semplice, perchè in base alla scelta effettuata da un menu che si trova in un file esterno (menu.asp) visualizza i dati richiesti formattandoli in una tabella con una semplice SELECT (Linguaggio SQL).
L'unica query interessante da analizzare è:
SQL = "SELECT TOP 20 * FROM ip ORDER BY ordine DESC"
Con l'utilizzo della clausola TOP 20 riusciamo a ricavare solo i primi 20 record, che saranno ordina in modo Decrescente (dall'ultimo) grazie all'utilizzo di DESC nella clausola ORDER BY (ordina per un determinato o piu' campi).
Connessione al database
La connessione al database è la classica connessione ADO
dim objConn, DSNtempset objConn= server.CreateObject("ADODB.Connection")DSNtemp="DRIVER={Microsoft Access Driver (*.mdb)}; "DSNtemp=DSNtemp & "DBQ=" & server.mappath("db/wmpstat.mdb")objConn.Open DSNtemp
in cui l'indirizzo del database viene indicato con un percorso relativo, che verrà trasformato in assoluto grazie all'utilizzo della funzione Server.MapPath.