Introduction
Quand on héberge un site web sur un VPS, on a accès à une mine d'informations dans les logs du serveur : qui visite le site, quelles pages sont consultées, combien de bots passent, quels codes HTTP sont retournés... Mais personne n'a envie de se connecter en SSH chaque matin pour lire des fichiers de logs bruts.
Dans cet article, nous allons mettre en place un système automatisé qui :
- Analyse les logs Apache pour en extraire des statistiques utiles
- Distingue les visiteurs humains des bots
- Génère un rapport HTML stylisé (comme un mini dashboard)
- L'envoie automatiquement par email chaque semaine via un cron job
Le tout avec un seul script Python, sans aucune dépendance externe.
Le Contexte
On part d'un VPS Linux (Ubuntu) hébergeant un site web derrière Apache. La configuration :
- Serveur : VPS Ubuntu 22.04
- Serveur web : Apache 2.4 (site statique) + Nginx/Docker (application Laravel)
- Logs Apache : format Combined Log, écrit dans
/var/log/apache2/access.log - Email : compte SMTP OVH pour l'envoi
L'objectif : recevoir chaque lundi matin un email récapitulant l'activité de la semaine passée, sans installer de logiciel supplémentaire sur le serveur.
Étape 1 : Comprendre le format des logs Apache
Apache écrit une ligne par requête dans son access log. En format Combined, chaque ligne ressemble à :
192.168.1.42 - - [10/Feb/2026:14:30:17 +0000] "GET /blog HTTP/1.1" 200 11262 "https://monsite.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/144.0.0.0"Voici ce que contient chaque champ :
| Champ | Exemple | Description |
|---|---|---|
| IP | 192.168.1.42 | Adresse du visiteur |
| Date | [10/Feb/2026:14:30:17] | Horodatage de la requête |
| Méthode + URL | GET /blog | Page demandée |
| Code HTTP | 200 | Succès, erreur 404, redirection... |
| Taille | 11262 | Octets envoyés |
| Referer | https://monsite.com/ | D'où vient le visiteur |
| User-Agent | Mozilla/5.0 ... Chrome | Navigateur ou bot |
Étape 2 : Parser les logs avec Python
On utilise une regex pour extraire chaque champ. Aucune bibliothèque externe n'est nécessaire : re, collections.Counter et datetime suffisent.
import re
from collections import Counter
from datetime import datetime, timedelta
LOG_FILE = "/var/log/apache2/access.log"
# Regex pour le format Apache Combined
log_pattern = re.compile(
r'^(\S+) \S+ \S+ \[([^\]]+)\] "(\S+) (\S+) \S+" (\d{3}) (\d+|-) "(.*?)" "(.*?)"'
)
# Ne garder que les 7 derniers jours
seven_days_ago = datetime.now() - timedelta(days=7)
visits_per_day = Counter()
pages = Counter()
ips = Counter()
http_codes = Counter()
with open(LOG_FILE, "r", errors="ignore") as f:
for line in f:
match = log_pattern.match(line)
if not match:
continue
ip, date_raw, method, path, code, size, referer, ua = match.groups()
# Parser la date
dt = datetime.strptime(date_raw.split()[0], "%d/%b/%Y:%H:%M:%S")
if dt < seven_days_ago:
continue
visits_per_day[dt.strftime("%Y-%m-%d")] += 1
pages[path] += 1
ips[ip] += 1
http_codes[code] += 1En quelques lignes, on a déjà les visites par jour, les pages les plus vues, les IPs les plus actives et la répartition des codes HTTP.
Étape 3 : Séparer les humains des bots
C'est un point crucial. Sur un site typique, 50 à 70% du trafic provient de bots (crawlers Google, scanners, scripts automatisés). Si on ne filtre pas, les statistiques sont faussées.
BOT_PATTERNS = [
"bot", "crawler", "spider", "curl", "python", "wget",
"scrapy", "semrush", "ahrefs", "gptbot",
"facebookexternalhit", "google-firebase", "mediapartners"
]
def is_bot(user_agent):
ua_lower = user_agent.lower()
if ua_lower in ("-", ""):
return True
return any(pattern in ua_lower for pattern in BOT_PATTERNS)
# Utilisation dans la boucle de parsing
human_visits = 0
for line in lines:
# ... parsing ...
if not is_bot(ua):
human_visits += 1Cette approche par mots-clés est simple mais efficace. Les principaux bots sont détectés :
| Bot | Source | Rôle |
|---|---|---|
| Googlebot | Indexation Google Search | |
| Bingbot | Microsoft | Indexation Bing |
| GPTBot | OpenAI | Entraînement IA |
| Mediapartners-Google | AdSense | |
| curl / Python-urllib | Scripts | Requêtes automatisées |
| SemrushBot / AhrefsBot | SEO Tools | Analyse SEO |
Étape 4 : Générer un email HTML stylisé
Plutôt qu'un email texte brut illisible, on génère un rapport HTML avec des tableaux, des couleurs et des indicateurs visuels. Python le permet nativement avec email.mime.
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
bot_pct = round((total - human_visits) / max(total, 1) * 100, 1)
# Construction du HTML avec des stat-boxes
html = "<html><body>"
html += "<h1>Rapport de Visites</h1>"
html += f"<div class='stat-box'>{total:,} requêtes</div>"
html += f"<div class='stat-box'>{human_visits:,} humains</div>"
html += f"<div class='stat-box'>{bot_pct}% bots</div>"
# ... tableaux de stats ...
html += "</body></html>"
msg = MIMEMultipart("alternative")
msg["Subject"] = f"[Rapport] Visites monsite.com - {date}"
msg["From"] = "noreply@monsite.com"
msg["To"] = "admin@monsite.com"
msg.attach(MIMEText(texte_brut, "plain"))
msg.attach(MIMEText(html, "html"))Astuce : Envoyer à la fois une version texte et une version HTML (via MIMEMultipart("alternative")) garantit que le mail s'affiche correctement partout, même sur les clients mail qui ne supportent pas le HTML.Étape 5 : Envoyer avec smtplib (SMTP sécurisé)
L'envoi se fait via le module natif smtplib en connexion SSL. Aucune dépendance externe à installer.
import smtplib
import ssl
SMTP_HOST = "ssl0.ovh.net"
SMTP_PORT = 465
SMTP_USER = "noreply@monsite.com"
SMTP_PASS = "VotreMotDePasse"
context = ssl.create_default_context()
with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, context=context) as server:
server.login(SMTP_USER, SMTP_PASS)
server.sendmail(SMTP_USER, "admin@monsite.com", msg.as_string())
print("Rapport envoyé !")Les fournisseurs courants et leurs paramètres SMTP :
| Fournisseur | Hôte SMTP | Port | Sécurité |
|---|---|---|---|
| OVH | ssl0.ovh.net | 465 | SSL |
| Gmail | smtp.gmail.com | 587 | STARTTLS |
| Outlook | smtp-mail.outlook.com | 587 | STARTTLS |
| Infomaniak | mail.infomaniak.com | 465 | SSL |
Étape 6 : Automatiser avec cron
Le script est prêt, il ne reste qu'à l'exécuter automatiquement. cron est le planificateur de tâches natif de Linux.
# Ouvrir l'éditeur crontab
crontab -e
# Ajouter cette ligne pour un envoi chaque lundi à 8h
0 8 * * 1 /usr/bin/python3 /home/deployer/report_visits.py >> /home/deployer/report.log 2>&1Comprendre la syntaxe cron
# Format :
# minute heure jour_du_mois mois jour_semaine commande
# Exemples :
0 8 * * 1 # Chaque lundi à 8h00
0 */6 * * * # Toutes les 6 heures
30 9 1 * * # Le 1er de chaque mois à 9h30
0 8 * * 1-5 # Du lundi au vendredi à 8hBonnes pratiques cron
- Toujours rediriger les sorties vers un fichier log (
>> fichier.log 2>&1) - Utiliser les chemins absolus (
/usr/bin/python3, pas justepython3) - Vérifier avec
crontab -lque la tâche est bien enregistrée - Tester manuellement le script avant de l'automatiser
Étape 7 : Ajouter des alertes intelligentes
Le rapport peut aussi inclure des alertes conditionnelles. Par exemple, surveiller l'espace disque :
import subprocess
result = subprocess.run(["df", "-h", "/"], capture_output=True, text=True)
disk_line = result.stdout.strip().split("\n")[-1].split()
disk_pct = int(disk_line[4].replace("%", ""))
if disk_pct > 90:
alert = "Disque critique : " + str(disk_pct) + "%"
# Ajouter l'alerte dans le HTML du rapportOn peut aussi détecter des anomalies :
- Pic d'erreurs 404 : possible attaque ou liens cassés
- IP avec un nombre anormal de requêtes : possible bot malveillant ou DDoS
- Augmentation soudaine du trafic : article viral ou attaque
Résultat : ce que vous recevez
Chaque lundi, un email arrive avec :
- Indicateurs clés : requêtes totales, visites humaines, pourcentage de bots
- Visites par jour : tableau des 7 derniers jours
- Top 10 pages : les pages les plus consultées (hors assets CSS/JS/images)
- Top 10 IPs : pour identifier les visiteurs réguliers ou les bots agressifs
- Navigateurs : répartition Chrome, Firefox, Edge, Mobile...
- Codes HTTP : 200 (succès), 404 (pages introuvables), 301 (redirections)...
- Alerte disque : avertissement si l'espace est critique
Le tout dans un email HTML propre, lisible directement sur mobile ou desktop.
Aller plus loin
Combiner avec d'autres sources
Ce script peut être étendu pour inclure :
- Les logs Docker (
docker logs) pour une application conteneurisée - Les stats Firebase Analytics ou Google Analytics via leurs APIs
- Le monitoring serveur (RAM, CPU, uptime)
Alternative : GoAccess
GoAccess est un outil dédié à l'analyse de logs en temps réel. Il génère des rapports HTML interactifs. Cependant, notre approche Python a l'avantage d'être :
- Entièrement personnalisable
- Sans dépendance à installer
- Intégrable facilement avec l'envoi d'email
Récapitulatif
| Composant | Outil | Rôle |
|---|---|---|
| Parsing des logs | Python re + Counter | Extraire les statistiques |
| Détection des bots | Liste de patterns User-Agent | Séparer humains et bots |
| Génération du rapport | Python email.mime | Email HTML stylisé |
| Envoi | Python smtplib (SSL) | SMTP sécurisé |
| Automatisation | cron | Exécution hebdomadaire |
| Alertes | Python subprocess | Surveillance disque et anomalies |
Conclusion
Avec un seul script Python d'environ 200 lignes et un cron job, on obtient un système de reporting complet qui rivalise avec des outils bien plus complexes. L'avantage majeur : zéro dépendance externe. Tout repose sur la bibliothèque standard de Python et les outils natifs de Linux.
Cette approche est réutilisable sur n'importe quel VPS avec Apache ou Nginx. Il suffit d'adapter le chemin du fichier de log et la regex de parsing si le format diffère.
Conseil : Commencez par lancer le script manuellement pour vérifier que le rapport est correct, puis ajoutez-le à cron une fois validé. Et n'oubliez pas de toujours rediriger les sorties vers un fichier log pour faciliter le débogage en cas de problème.