Pourquoi j'ai codé mon propre crawler SEO

Pour préparer la migration, j'ai construit un crawler local avec un tableau filtrable et exportable. Voici le besoin auquel il répondait, et les deux bugs rencontrés pendant sa mise au point.

Jim Sagnier 5 min de lecture Mis à jour le
Web & outilsEurofiscalis : les outils · épisode 1
L’essentiel en bref

Pour préparer la migration d'un site d'environ 2700 pages, j'avais besoin d'un inventaire : les adresses, les titres, les statuts HTTP et les redirections. Les exports que j'avais obtenus ne me donnaient pas le tableau que je cherchais. J'ai donc construit un crawler local pour ce travail.

Je suis Jim. Petit aparté dans la saga de la refonte Eurofiscalis, sur un outil que je me suis fabriqué en chemin. Quand tu migres un site de 2700 pages en 7 langues, la première chose qu'il te faut, c'est un inventaire propre de l'existant : chaque page, son adresse, son titre, son statut. Je raconte ici comment j'ai obtenu cet inventaire, avec les deux bugs rencontrés en chemin.

Pourquoi pas un outil du commerce

J'avais essayé SiteOne et Screaming Frog avant de construire l'outil.

Avec SiteOne, j'avais obtenu des dizaines de mégaoctets de JSON et un rapport HTML lourd à exploiter. Je n'en tirais pas facilement le tableau filtrable dont j'avais besoin. Screaming Frog proposait cette interface de tableau, mais sa version gratuite est limitée à 500 URL. Il couvrait aussi beaucoup plus de contrôles que ceux que je voulais faire à ce moment-là.

J'avais déjà le sitemap, avec ses 2799 adresses. Il donnait la liste des pages à visiter, pas leur contenu ni leur statut. Il fallait encore les lire et rassembler les résultats dans un tableau propre, filtrable et exportable, avec les colonnes utiles à la migration.

Ce que fait le crawler

Le résultat est une petite application locale, sans prétention. Tu colles une adresse, tu lances, et tu obtiens un tableau que tu peux trier, filtrer, et exporter en Excel ou en CSV. Deux modes : partir du sitemap, ou explorer le site de lien en lien.

Pour chaque page, il extrait ce qui compte vraiment pour une migration : la langue, le statut HTTP, le H1, le titre, la méta description, chacun avec son compteur de caractères, les balises hreflang, et la cible de redirection quand il y en a une.

Il embarque même un petit audit en prime : une colonne d'alertes qui signale un titre absent, trop long ou trop court, une description qui déborde, un H1 manquant. Rien de sorcier, mais ça transforme un simple inventaire en outil de contrôle qualité.

La stack, minimaliste

Sous le capot, c'est volontairement pauvre. Du Python avec Flask, un seul script pour tout démarrer, et une page web locale. Pas de navigateur headless, pas de rendu JavaScript : les pages du site sont générées côté serveur, donc une requête HTTP permet de lire les informations présentes dans leur HTML. L’outil ne voit pas un contenu ajouté uniquement par JavaScript.

J'ai construit ça proprement, en séparant les responsabilités (un module crawle, un module extrait, un module exporte) et en écrivant les tests avant le code. Mais j'ai surtout appliqué une règle stricte : ne pas reconstruire Screaming Frog. Pas de scoring SEO avancé, pas de rendu JS, pas de multi-utilisateur. Juste le tableau dont j'avais besoin, rien de plus. Cette limite de périmètre m'a permis de rester concentré sur l'inventaire.

Le bug qui gelait le navigateur

Premier bug, et première leçon. Pour afficher la progression d'un crawl, la page interroge le serveur en boucle. Au début, à chaque interrogation, le serveur re-sérialisait la totalité de l'état, toutes les pages déjà trouvées comprises.

Sur un petit site, le problème ne se voyait pas. Avec plusieurs milliers d'adresses, le navigateur se figeait, et pire, le crawl lui-même se bloquait, parce que ce gros paquet de données se construisait au mauvais endroit et coinçait tout le reste.

Le fix tient en une idée : ne renvoyer que ce qui a changé depuis la dernière fois, pas tout l'état à chaque coup. Le volume transféré à chaque sondage restait ainsi limité aux nouvelles données.

Le faux positif qui falsifiait tout

Le deuxième bug faussait les résultats sans interrompre le crawl. Le site en préparation était protégé par un mot de passe. Le crawler sait le franchir. Mais quand le mot de passe fourni était faux, la protection ne renvoyait pas une erreur. Elle renvoyait une page "Accès restreint", avec un statut qui disait "tout va bien".

Résultat : le crawler enregistrait tranquillement cette page "Accès restreint" à la place de chaque vraie page. Un export de contrôle qualité entièrement faux. Et le pire, c'est qu'aucune alerte ne se déclenchait. Tout paraissait vert.

Le correctif était simple : si aucun jeton d'authentification n'est posé, on lève une erreur qui remonte jusqu'à l'écran. Bon mot de passe, ça passe. Mauvais, ça s'arrête net.

Ce que ça a donné

Au bout du compte, un inventaire réel et propre : 2799 adresses uniques, en 7 langues, dans un tableau filtrable. C'est ce tableau qui a servi de base au plan de redirections (grâce à la colonne des cibles de redirection) et à l'audit qualité (grâce à la colonne d'alertes) pendant toute la bascule.

L'application a été construite en quelques sessions autour de ce tableau. Elle ne remplace pas un audit SEO complet : elle me donnait les informations à trier pour cette bascule, avec un export que je pouvais exploiter.

Compléter l'inventaire avec Search Console

Le crawler lit les pages accessibles. Pour connaître l'état d'indexation enregistré par Google, j'ai utilisé Search Console. Les deux outils ne répondent pas à la même question. Je détaille ce branchement dans l'article suivant : brancher Search Console sur Claude, et la vérité sur l'indexation.