Bash - Compteur de visites avec les logs

Ce script Bash analyse les fichiers access logs Nginx pour obtenir des statistiques de fréquentation mensuelles sur une année donnée.

Il distingue les visites humaines des bots et compte le nombre d’adresses IP uniques par mois et par type de visite.

Le script est RGPD-friendly, aucune IP n’est stockée ou utilisée en dehors du comptage temporaire. Les fichiers statiques (CSS, JS, images, polices) sont ignorés afin que le comptage humain reflète des visites réelles sur les pages. Il fonctionne avec awk standard sur Debian, sans dépendances supplémentaires. Les statistiques sont affichées sous forme de tableau lisible avec séparateurs par mois.

Affichage final :

-------------------------------
Mois | Type | IP uniques
-------------------------------
Jan/2026 | bot | 737
Jan/2026 | human | 561
-------------------------------
Fev/2026 | bot | 707
Fev/2026 | human | 509
-------------------------------
Mar/2026 | bot | 659
Mar/2026 | human | 624
-------------------------------

Script

Ce script :

  • Lit tous les logs Nginx de l’année
  • Détecte bots VS humains via User-Agent et URL
  • Compte IP uniques par mois et par type
  • Affiche un tableau avec séparateurs
  • Ignore les fichiers statiques pour éviter d’augmenter la stat “humains”
  • RGPD-friendly : pas de stockage d’IP en base, juste comptage temporaire
#!/bin/bash
# ------------------------------- #
# Compteur visite humans and bots #
# ------------------------------- #

# --------- #
# VARIABLES #
# --------- #

# Detecter les bots
HUMAN_BOTS='bot|crawl|spider|slurp|bingpreview|yandex|duckduckbot|baiduspider|curl|wget|python|go-http|java|httpclient|scanner|monitor'
# Ignorer des fichiers pour eviter les repetitions
STATIC_EXT='.((css|js|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|map))$'
# Année à analyser
YEAR='2026'


# --------- #
# MAIN #
# --------- #

awk -v year="$YEAR" -v bots="$HUMAN_BOTS" -v static="$STATIC_EXT" '
# Affiche le header du tableau avec une ligne de séparation
# %-10s et %-6s : formatage pour aligner les colonnes
BEGIN {
print "-------------------------------"
printf "%-10s | %-6s | %s\n", "Mois", "Type", "IP uniques"
print "-------------------------------"
}
{
# découpe la date en tableau et extrait les données de l'année
split($4, d, "/")
month = d[2]
year_log = substr(d[3], 1, 4)

if (year_log != year) next

# Compare l'IP et la page (URL)
ip = $1
url = $7
ua = tolower($0)

# Formate pour l'affichage
key_month = month "/" year

# Determine si Bots ou Humains
if (ua ~ bots) {
type = "bot"
} else if (url !~ static) {
type = "human"
} else {
next
}

# l'IP est comptée une seule fois
key = key_month "|" type "|" ip
visits[key] = 1
}
END {
# Comptage IP uniques
for (k in visits) {
split(k, a, "|")
count[a[1] "|" a[2]]++
}

# Trier par mois
n = asorti(count, sorted)

prev_month = ""

# Afficher les données triées
for (i = 1; i <= n; i++) {
split(sorted[i], b, "|")
month = b[1]
type = b[2]

if (month != prev_month) {
if (prev_month != "") {
print "-------------------------------"
}
prev_month = month
}

printf "%-10s | %-6s | %d\n", month, type, count[sorted[i]]
}

print "-------------------------------"
}
# Fichier log Nginx à analyser
' /var/log/nginx/access.log

Explication détaillée

Shebang

Indique que le script doit être exécuté avec bash :

#!/bin/bash

Variables

Liste de mots-clés pour détecter les bots et les scanners dans le User-Agent. Le | signifie “ou” dans la regex.

HUMAN_BOTS='bot|crawl|spider|slurp|bingpreview|yandex|duckduckbot|baiduspider|curl|wget|python|go-http|java|httpclient|scanner|monitor'

Regex pour ignorer les requêtes sur les fichiers statiques (CSS, JS, images, polices). Utile pour ne compter que les vraies visites humaines (pas chaque image ou script).

STATIC_EXT='.((css|js|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|map))$'

Année à analyser pour filtrer uniquement les logs de cette année precise.

YEAR='2026'

Main program

Traitement des variables

Lance la commande awk. L’option -v var=value permet de passer les variables year, bots et static dans awk .

awk -v year="$YEAR" -v bots="$HUMAN_BOTS" -v static="$STATIC_EXT" '

Affichage du header du tableau

La section BEGIN s’exécute avant de lire les logs. Elle affiche le header du tableau avec une ligne de séparation. Les %-10s et %-6s sont des formatages pour aligner les colonnes.

BEGIN {
print "-------------------------------"
printf "%-10s | %-6s | %s\n", "Mois", "Type", "IP uniques"
print "-------------------------------"
}

Traitement et mise en forme des données des logs

Cette partie fait le traitement sur le fichier de logs Ngnix.

  • $4 : champ de la date dans le log Nginx ([09/Jan/2026:12:34:56 +0000]).
  • split($4, d, "/") : découpe la date en tableau d par /.
  • d[2] : mois (Jan)
  • d[3] : année + heure (2026:12:34:56)
  • substr(d[3],1,4) : extrait uniquement l’année.
{
split($4, d, "/")
month = d[2]
year_log = substr(d[3], 1, 4)

if (year_log != year) next

Cette partie récupère les information du fichier de logs Ngnix et les traite.

  • $1 : l’IP du visiteur
  • $7 → : la page demandée (URL)
  • tolower($0) : met la ligne en minuscule pour simplifier la détection de bots
if (year_log != year) next

ip = $1
url = $7
ua = tolower($0)

Prépare une clé “mois/année” pour le regroupement, ex: Jan/2026.

key_month = month "/" year

Détermine le type de visite :

  • Si User-Agent correspond à la liste bots : bot
  • Sinon, si l’URL n’est pas un fichier statique : human
  • Sinon : ignore la ligne (next)
if (ua ~ bots) {
type = "bot"
} else if (url !~ static) {
type = "human"
} else {
next
}

Crée une clé unique par mois, type et IP. En mettant visits[key] = 1, une IP est comptée une seule fois par mois/type.

key = key_month "|" type "|" ip
visits[key] = 1

La section END, après avoir parcouru tous les logs permet pour chaque IP unique, d’incrémenter un compteur par mois et type (count[mois|type]).

END {
# Comptage IP uniques
for (k in visits) {
split(k, a, "|")
count[a[1] "|" a[2]]++
}

Affichage des données

Cette partie mets en place l’affichage du résultat. La commande asorti(count, sorted) trie les clés du tableau count par ordre alphabétique
La boucle for permet d’afficher les données triées sépare par mois (prev_month) et ajoute une ligne de séparation

    n = asorti(count, sorted)

prev_month = ""

for (i = 1; i <= n; i++) {
split(sorted[i], b, "|")
month = b[1]
type = b[2]

if (month != prev_month) {
if (prev_month != "") {
print "-------------------------------"
}
prev_month = month
}

printf "%-10s | %-6s | %d\n", month, type, count[sorted[i]]
}

print "-------------------------------"
}

Choix du fichier à traiter

Fichier log Nginx à analyser.

' /var/log/nginx/access.log

Documentation

Man

🡅 Partager