Un outil de monitoring avec Google Sheets et Claude AI

Résumer ce contenu avec votre LLM préféré !

Il arrive parfois que l’on ait un besoin rapide d’outils techniques, mais que les solutions proposées sur le marché soient « surdimensionnées ». C’est le cas des outils de monitoring de sites, qui proposent usuellement beaucoup de fonctionnalités, mais qui, pour une personne qui possède une dizaine de sites, s’avèrent totalement démesurés, et accessoirement payants.

La solution passe donc par la création d’un système adapté à ses besoins, et c’est ce que je vous propose ici. Pour ce faire, deux outils gratuits : Google Sheets et Claude AI !

Quels besoins de monitoring pour un éditeur de site ou une TPE ?

Pour une petite structure, nul besoin d’outil permettant de surveiller des tas de critères : l’essentiel est de s’assurer que les sites ne sont pas en panne, et que les deux principaux éléments liés au SEO sont toujours fonctionnels : le robots.txt, et la balise Title (la plus importante d’un point de vue sémantique) sur les pages les plus stratégiques.

Voici donc ce que permettra notre petit outil :

  • Vérifier les codes HTTP pour détecter les pannes ou erreurs
  • Surveiller les fichiers robots.txt pour repérer toute modification (ajout ou suppression de lignes)
  • Monitorer les balises Title pour s’assurer qu’aucun changement non souhaité n’a lieu sur les pages stratégiques positionnées en SEO
  • Recevoir des alertes email automatiques en cas de problème

Le tout doit pouvoir « tourner » en automatique et se déclencher à intervales réguliers. Pour cela, Google Sheets offre un outil intéressant au niveau d’Appscript (le langage de programmation intégré à la solution) : les triggers. Ce sont des déclencheurs que l’on peut paramétrer pour exécuter des fonctions avec des intervales précis, par exemple toutes les 4 heures.

1ère étape : construire l’interface dans Google Sheets

La construction de l’interface dans Google Sheets va permettre de connaître les positions des principales données que nous devrons utiliser dans le code. Cela permettra notamment de donner suffisamment d’informations à Claude pour qu’il puisse sans erreurs, coder notre petit outil pour nous !

J’ai décidé de créer deux onglets, l’un pour monitorer les homepages des sites, l’autre pour surveiller les changements intempestifs de Titles sur des URL stratégiques. En matière de mise en forme, on reste dans du classique mais très lisible et agréable à l’oeil !

Une fois notre interface construite, passons dans Claude AI pour définir très précisément les besoins de notre futur outil !

2ème étape : écrire le prompt dans Claude

Le prompt est ce qui ici fait toute la différence. Si un néophyte pourra réussir à créer un tel outil, un développeur aura, lui, la logique et les connaissances nécessaires pour expliquer à Claude « comment » architecturer l’application. Et c’est un avantage indéniable, car comme vous le verrez dans la vidéo, il m’a grâce à cela généré le code en « one shot », sans aucune erreur !

Plutôt que de longs discours, voici le prompt que je lui ai donné :

Texte brut
Tu es développeur Google Appscript et tu dois développer un outil de monitoring pour surveiller des sites internet.  Cet outil doit permettre de checker le code HTTP d'une liste d'URL, et de surveiller les changements survenant potentiellement dans le robots.txt.  Une second onglet permet de surveiller les changements de la balise Title d'une liste d'URL différente du premier onglet.  Le premier onglet est appelé "Monitoring sites". Colonne A : "URL homepage" Colonne B : "Statut HTTP" Colonne C : "Robots.txt" Colonne D : "Changement robots.txt ?"  La liste des sites débute ligne 6.  Le second onglet est appelé "Monitoring Titles" Colonne A : "URL" Colonne B : "Title" Colonne C : "Changement ?"  Tant qu'il existe des valeurs dans la colonne A de chaque onglet, tu dois réaliser le traitement pour chaque URL renseignée.  Tous les paramètres doivent être stockés dans un objet dédié "PARAM" de manière à pouvoir modifier les colonnes simplement sans retoucher tout le code.  Utilise un trigger pour réaliser les vérifications de manière programmée, avec un intervalle de 4 heures par défaut (variable à intégrer dans PARAM).  Si des changements ont lieu, tu devras envoyer un mail à l'adresse indiquée dans la variable dans PARAM, par défaut "cgirard10110@gmail.com"  Règles de gestion : * lors de la première évaluation, tu vas checker le statut HTTP et inscrire ce dernier dans la colonne dédiée du 1er onglet. Fais de même avec le robots.txt (tu feras un getContent et tu iras inscrire le contenu du robots.txt dans la case dédiée). Dans le second onglet, tu iras inscrire la valeur de la balise Title de chaque URL dans la colonne dédiée. * lors des exécutions suivantes : si le code HTTP est différent de 200 et est différent de 503, tu ajouteras dans un log l'événement avec l'URL concernée et l'erreur rencontrée). S'il y a un changement dans le robots.txt, communique les lignes qui ont été supprimées et celles ajoutées, de la même manière. indique dans la colonne "Changement robots.txt ?" qu'il y a eu un changement. Dans l'onglet "Monitoring Titles", fais de même avec les Titles.  Ajoute un menu "Monitoring" permettant de lancer le monitoring via l'option de menu "Lancer le monitoring", et de l'arrêter via l'option "Arrêter le monitoring".  Le statut du monitoring doit être indiqué dans la cellule B2 de l'onglet "Monitoring sites".  Pense à utiliser au maximum des variables dans l'objet PARAM pour tous les paramètres que l'on utilise dans le code.

Le prompt est long, mais détaillé et donne absolument tous les éléments dont Claude aura besoin pour créer un code parfaitement adapté aux sheets que nous avons construit précédemment. Et c’est ce qui a permis au LLM d’écrire un code clair, structuré et parfaitement cohérent par rapport aux besoins exprimés.

Voici le code Appscripts que Claude a généré, et que nous pouvons donc utiliser dans votre projet Google Sheets :

JavaScript
// ======================================== // CONFIGURATION GLOBALE // ======================================== const PARAM = {   // Noms des onglets   SHEET_MONITORING: "Monitoring sites",   SHEET_TITLES: "Monitoring Titles",      // Colonnes Monitoring sites   COL_URL_HOMEPAGE: 1, // A   COL_STATUT_HTTP: 2, // B   COL_ROBOTS: 3, // C   COL_CHANGEMENT_ROBOTS: 4, // D      // Colonnes Monitoring Titles   COL_URL_TITLE: 1, // A   COL_TITLE: 2, // B   COL_CHANGEMENT_TITLE: 3, // C      // Lignes de départ   START_ROW_MONITORING: 6,   START_ROW_TITLES: 6,      // Cellule statut monitoring   CELL_STATUT: "B2",      // Email   EMAIL_DESTINATION: "cgirard10110@gmail.com",      // Intervalle de déclenchement (en heures)   TRIGGER_INTERVAL: 4,      // Nom du trigger   TRIGGER_NAME: "monitoringTrigger",      // Codes HTTP acceptables   HTTP_CODE_OK: 200,   HTTP_CODE_MAINTENANCE: 503 };  // ======================================== // MENU PERSONNALISÉ // ======================================== function onOpen() {   const ui = SpreadsheetApp.getUi();   ui.createMenu('Monitoring')     .addItem('Lancer le monitoring', 'lancerMonitoring')     .addItem('Arrêter le monitoring', 'arreterMonitoring')     .addToUi(); }  // ======================================== // GESTION DES TRIGGERS // ======================================== function lancerMonitoring() {   // Supprimer les anciens triggers   supprimerTriggers();      // Créer un nouveau trigger   ScriptApp.newTrigger('executerMonitoring')     .timeBased()     .everyHours(PARAM.TRIGGER_INTERVAL)     .create();      // Mettre à jour le statut   updateStatutMonitoring("Actif");      // Lancer une première exécution   executerMonitoring();      SpreadsheetApp.getUi().alert('Monitoring lancé avec succès !'); }  function arreterMonitoring() {   supprimerTriggers();   updateStatutMonitoring("Inactif");   SpreadsheetApp.getUi().alert('Monitoring arrêté.'); }  function supprimerTriggers() {   const triggers = ScriptApp.getProjectTriggers();   triggers.forEach(trigger => {     if (trigger.getHandlerFunction() === 'executerMonitoring') {       ScriptApp.deleteTrigger(trigger);     }   }); }  function updateStatutMonitoring(statut) {   const ss = SpreadsheetApp.getActiveSpreadsheet();   const sheet = ss.getSheetByName(PARAM.SHEET_MONITORING);   sheet.getRange(PARAM.CELL_STATUT).setValue(statut); }  // ======================================== // FONCTION PRINCIPALE DE MONITORING // ======================================== function executerMonitoring() {   const logs = [];      try {     // Monitoring des sites     const logsMonitoring = monitorerSites();     logs.push(...logsMonitoring);          // Monitoring des titles     const logsTitles = monitorerTitles();     logs.push(...logsTitles);          // Envoyer email si des changements détectés     if (logs.length > 0) {       envoyerEmailNotification(logs);     }        } catch (error) {     Logger.log('Erreur lors du monitoring : ' + error.toString());     logs.push('Erreur générale : ' + error.toString());     envoyerEmailNotification(logs);   } }  // ======================================== // MONITORING DES SITES // ======================================== function monitorerSites() {   const ss = SpreadsheetApp.getActiveSpreadsheet();   const sheet = ss.getSheetByName(PARAM.SHEET_MONITORING);   const logs = [];      const lastRow = sheet.getLastRow();      for (let row = PARAM.START_ROW_MONITORING; row <= lastRow; row++) {     const url = sheet.getRange(row, PARAM.COL_URL_HOMEPAGE).getValue();          // Si pas d'URL, on arrête     if (!url || url.toString().trim() === '') {       break;     }          try {       // Récupérer le statut HTTP       const response = UrlFetchApp.fetch(url, {         muteHttpExceptions: true,         followRedirects: false       });              const httpCode = response.getResponseCode();       const ancienCode = sheet.getRange(row, PARAM.COL_STATUT_HTTP).getValue();              // Mettre à jour le code HTTP       sheet.getRange(row, PARAM.COL_STATUT_HTTP).setValue(httpCode);              // Vérifier si le code HTTP est problématique       if (httpCode !== PARAM.HTTP_CODE_OK && httpCode !== PARAM.HTTP_CODE_MAINTENANCE) {         logs.push(`⚠️ [${url}] Code HTTP anormal : ${httpCode}`);         sheet.getRange(row, PARAM.COL_CHANGEMENT_ROBOTS).setValue('Erreur HTTP');       }              // Récupérer et vérifier robots.txt       if (httpCode === PARAM.HTTP_CODE_OK) {         const robotsUrl = getRobotsUrl(url);         const logsRobots = verifierRobotsTxt(robotsUrl, sheet, row);         logs.push(...logsRobots);       }            } catch (error) {       logs.push(`❌ [${url}] Erreur : ${error.toString()}`);       sheet.getRange(row, PARAM.COL_CHANGEMENT_ROBOTS).setValue('Erreur');     }          // Pause pour éviter les limites de taux     Utilities.sleep(1000);   }      return logs; }  function getRobotsUrl(url) {   const urlObj = url.match(/^(https?:\/\/[^\/]+)/);   return urlObj ? urlObj[1] + '/robots.txt' : null; }  function verifierRobotsTxt(robotsUrl, sheet, row) {   const logs = [];      if (!robotsUrl) return logs;      try {     const response = UrlFetchApp.fetch(robotsUrl, {       muteHttpExceptions: true     });          if (response.getResponseCode() === 200) {       const nouveauRobots = response.getContentText();       const ancienRobots = sheet.getRange(row, PARAM.COL_ROBOTS).getValue();              if (ancienRobots === '' || ancienRobots === null) {         // Première exécution         sheet.getRange(row, PARAM.COL_ROBOTS).setValue(nouveauRobots);         sheet.getRange(row, PARAM.COL_CHANGEMENT_ROBOTS).setValue('Initial');       } else {         // Comparer les versions         if (ancienRobots !== nouveauRobots) {           const differences = comparerRobots(ancienRobots, nouveauRobots);           logs.push(`🔄 [${robotsUrl}] Changement détecté dans robots.txt :\n${differences}`);           sheet.getRange(row, PARAM.COL_ROBOTS).setValue(nouveauRobots);           sheet.getRange(row, PARAM.COL_CHANGEMENT_ROBOTS).setValue('Oui - ' + new Date().toLocaleString('fr-FR'));         } else {           sheet.getRange(row, PARAM.COL_CHANGEMENT_ROBOTS).setValue('Non');         }       }     }   } catch (error) {     logs.push(`❌ [${robotsUrl}] Erreur robots.txt : ${error.toString()}`);   }      return logs; }  function comparerRobots(ancienTexte, nouveauTexte) {   const anciennes = ancienTexte.split('\n');   const nouvelles = nouveauTexte.split('\n');      let differences = '';      // Lignes supprimées   const supprimees = anciennes.filter(ligne => !nouvelles.includes(ligne));   if (supprimees.length > 0) {     differences += '  ➖ Lignes supprimées :\n';     supprimees.forEach(ligne => {       if (ligne.trim() !== '') {         differences += `     - ${ligne}\n`;       }     });   }      // Lignes ajoutées   const ajoutees = nouvelles.filter(ligne => !anciennes.includes(ligne));   if (ajoutees.length > 0) {     differences += '  ➕ Lignes ajoutées :\n';     ajoutees.forEach(ligne => {       if (ligne.trim() !== '') {         differences += `     + ${ligne}\n`;       }     });   }      return differences; }  // ======================================== // MONITORING DES TITLES // ======================================== function monitorerTitles() {   const ss = SpreadsheetApp.getActiveSpreadsheet();   const sheet = ss.getSheetByName(PARAM.SHEET_TITLES);   const logs = [];      const lastRow = sheet.getLastRow();      for (let row = PARAM.START_ROW_TITLES; row <= lastRow; row++) {     const url = sheet.getRange(row, PARAM.COL_URL_TITLE).getValue();          // Si pas d'URL, on arrête     if (!url || url.toString().trim() === '') {       break;     }          try {       const response = UrlFetchApp.fetch(url, {         muteHttpExceptions: true       });              if (response.getResponseCode() === PARAM.HTTP_CODE_OK) {         const html = response.getContentText();         const nouveauTitle = extraireTitle(html);         const ancienTitle = sheet.getRange(row, PARAM.COL_TITLE).getValue();                  if (ancienTitle === '' || ancienTitle === null) {           // Première exécution           sheet.getRange(row, PARAM.COL_TITLE).setValue(nouveauTitle);           sheet.getRange(row, PARAM.COL_CHANGEMENT_TITLE).setValue('Initial');         } else {           // Comparer les titles           if (ancienTitle !== nouveauTitle) {             logs.push(`📝 [${url}] Changement de Title :\n  Ancien : "${ancienTitle}"\n  Nouveau : "${nouveauTitle}"`);             sheet.getRange(row, PARAM.COL_TITLE).setValue(nouveauTitle);             sheet.getRange(row, PARAM.COL_CHANGEMENT_TITLE).setValue('Oui - ' + new Date().toLocaleString('fr-FR'));           } else {             sheet.getRange(row, PARAM.COL_CHANGEMENT_TITLE).setValue('Non');           }         }       }            } catch (error) {       logs.push(`❌ [${url}] Erreur lors de la récupération du Title : ${error.toString()}`);       sheet.getRange(row, PARAM.COL_CHANGEMENT_TITLE).setValue('Erreur');     }          // Pause pour éviter les limites de taux     Utilities.sleep(1000);   }      return logs; }  function extraireTitle(html) {   const match = html.match(/<title[^>]*>(.*?)<\/title>/i);   return match ? match[1].trim() : 'Titre non trouvé'; }  // ======================================== // NOTIFICATION PAR EMAIL // ======================================== function envoyerEmailNotification(logs) {   if (logs.length === 0) return;      const sujet = `⚠️ Monitoring Sites - ${logs.length} changement(s) détecté(s)`;   const corps = `Bonjour,  Le monitoring automatique a détecté les changements suivants :  ${logs.join('\n\n')}  --- Date : ${new Date().toLocaleString('fr-FR')} Monitoring automatique - Google Apps Script`;      try {     MailApp.sendEmail({       to: PARAM.EMAIL_DESTINATION,       subject: sujet,       body: corps     });   } catch (error) {     Logger.log('Erreur lors de l\'envoi de l\'email : ' + error.toString());   } }

3ème étape : intégrer le code et tester !

L’opération est simplissime : il suffit de copier l’ensemble du code généré, et d’aller le coller dans l’interface de développement de Google Sheets.

Pour accéder aux outils de développement, passer par le menu « Extensions » puis « Apps Script » :

Une nouvelle fenêtre s’ouvre, permettant de créer le fichier .gs (pour « Google Script ») et d’y coller le code :

Enfin, un clic sur « Exécuter » permet d’initialiser le script dans notre Google Sheets (il faudra valider les accès de sécurité pour que ce dernier puisse s’exécuter avec votre profil Google).

Le bilan et la conclusion

En 15 minutes à peine, un peu de réflexion, quelques clics et une prompt bien écrit m’auront permis de créer un outil basique de monitoring de site internet. L’usage précis de la « promptologie » a permis au code de fonctionner immédiatement, sans nécessité de revenir au LLM pour rectifier quelque erreur ou oubli : de l’intérêt d’être le plus précis possible !

L’outil est parfaitement fonctionnel et c’est un Google Sheet très semblable qui me sert personnellement depuis des mois à « surveiller » mes sites. La méthode est simple, mais les possibilités sont incroyables quand on sait où aller avec des outils comme Google Sheets et consort !

Voir tous les articles de la catégorie Programmation

Ça pourrait vous intéresser !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Retour en haut