Nous developpons votre site vitrine SPA À PARTIR DE 300€. Oui, c'est dingue. Web SPA À PARTIR DE 300€ — Oui, c'est dingue. Parlons-en →

Comment éviter de faire bloquer votre IP quand plusieurs processus scrapent en même temps

Comment éviter de faire bloquer votre IP quand plusieurs processus scrapent en même temps

Tous ceux qui ont automatisé la consultation d'un site externe ont ajouté une pause entre les requêtes et considéré le problème réglé. Cela marche tant qu'un seul processus interroge. Quand votre application grandit et que la file d'attente, la requête web et une tâche planifiée interrogent en même temps, cette pause ne suffit plus et le blocage arrive. Voici pourquoi elle échoue et comment coordonner réellement, avec un cas réel qui nous a valu un 403. Les exemples sont en Laravel, l'une de nos spécialités, et nous comparons avec spatie/crawler, la référence de l'écosystème, mais la coordination entre processus se pose de la même façon avec n'importe quel stack.

Le 403 n'arrive pas parce que vous faites beaucoup de requêtes, mais parce que vous les faites en même temps

Presque tout le monde commence pareil : un sleep(1) entre les requêtes et en avant. Cela marche en local, en préproduction, et en production jusqu'au jour où le même site est consulté depuis trois endroits différents de votre application.

Cela nous est arrivé avec le moteur de jurisprudence du Conseil général du pouvoir judiciaire espagnol. Le commentaire laissé dans la configuration de production le raconte tel quel :

Le chat, le MCP et les trois workers de la file tirent sur poderjudicial.es sans se connaître. En dix-sept minutes, on est monté à une vingtaine de sessions de navigateur, parce que le modèle lance des recherches en parallèle sous des angles différents et que chacune retente trois fois. CENDOJ a fini par renvoyer 403.

Aucun de ces processus ne se comportait mal. Chacun respectait sa pause. Le problème, c'est que la pause était locale à chaque processus et que le site distant voit une seule adresse IP.

Un sleep entre requêtes n'est pas une limite de débit

C'est la partie difficile à voir. Si votre façon d'aller doucement consiste à dormir une seconde après chaque requête, ce que vous avez est une limite par processus. Avec cinq processus vous allez cinq fois plus vite sans vous en rendre compte.

La solution intuitive ne marche pas non plus : stocker quelque part l'heure de la dernière requête et attendre que l'intervalle s'écoule. Cela semble raisonnable et c'est une situation de concurrence de manuel. Trois processus qui se réveillent ensemble lisent la même réponse, attendent le même nombre de secondes et tirent au même instant. Vous avez transformé trois requêtes espacées en une rafale de trois, déguisée en requête espacée.

Ce qui marche, c'est d'inverser l'ordre : d'abord vous réservez le créneau, ensuite vous dormez. C'est la réservation qu'il faut protéger par un verrou, pas l'attente. Chaque processus demande sa place, reçoit un horodatage différent et dort jusqu'au sien. Trois processus simultanés sortent espacés sans jamais s'être parlé.

Ce calendrier partagé vit dans le cache de l'application, avec une entrée par cible. Avec Redis ou Memcached, la coordination traverse les machines ; avec le cache fichier, elle reste sur une seule.

Une fois bloqué, retenter à l'identique aggrave les choses

Une panne réseau et un 403 ne sont pas le même genre de problème, même si les deux signifient « ça n'a pas marché ».

Un 500 ou un 504 sont transitoires : vous attendez un peu et vous réessayez par le même chemin. Un 403 ou un 429 signifient que cette sortie est grillée. Réessayer par là après quinze secondes n'arrange rien et prolonge la punition.

Le traitement correct comporte deux volets. D'abord, noter cette sortie comme bloquée pendant une durée qui grandit si cela se reproduit, en partant de quelques minutes et en doublant jusqu'à un plafond. Ensuite, ne pas attendre avant de réessayer, parce que la nouvelle tentative sortira par une autre adresse, à laquelle le calendrier général s'applique déjà.

Et un détail qui évite d'insister pour rien : s'il ne reste aucune sortie saine, l'échec est accepté. Retenter contre l'adresse qui vient de vous refuser ne fait qu'allonger le blocage.

Andie recommande

Configurez la clé du calendrier par cible logique, pas par domaine. Dans notre cas il y en a deux, le moteur de recherche et le téléchargement de documents, sur le même site et avec des intervalles différents : dix secondes pour chercher et trois pour télécharger. En regroupant par domaine, la partie lente impose son rythme à la rapide sans raison.

Mentir sur le user agent vous trahit plus que d'y aller franchement

Falsifier le user agent fait partie des premiers réflexes quand on automatise des requêtes, et l'intuition est raisonnable : si vous ressemblez à un navigateur ordinaire, on vous traite comme tel. Ce que presque personne ne vérifie, c'est le nombre d'autres choses que le serveur regarde avant de décider s'il vous croit.

L'habitude est d'inventer une chaîne de user agent d'un Chrome ordinaire pour qu'on ne voie pas qu'il s'agit d'un navigateur automatisé. Le problème, c'est qu'un vrai Chrome n'envoie pas que cette chaîne : il envoie aussi les Client Hints, des en-têtes qui déclarent la marque et la version du navigateur, et il expose la même chose en JavaScript.

Ces données, c'est le navigateur réel qui les remplit, et on ne les falsifie pas en écrivant une chaîne. Donc quand vous inventez le user agent, vous obtenez une contradiction : un en-tête qui se dit Chrome 148 à côté de Client Hints qui disent autre chose. Ce n'est pas passer inaperçu, c'est lever la main.

Mieux vaut demander au navigateur sa vraie chaîne et lui retirer seulement le mot qui trahit le mode sans fenêtre. Tout le reste reste cohérent parce que cela l'est.

Séparer la requête de l'analyse change la façon de déboguer

Il y a un bénéfice moins spectaculaire qu'on apprécie au bout de six mois : distinguer deux types d'échec que l'on confond toujours.

Que la requête ne parte pas (le réseau, un délai dépassé, un statut d'erreur après les tentatives) est un échec de transport, et il est naturel qu'il lève une exception.

Que la page ne contienne pas ce que vous attendiez (un captcha, zéro résultat, la mise en page changée) n'est pas une erreur du programme, c'est une donnée. Et cela mérite de revenir comme résultat, pas comme exception.

Confondre les deux a des conséquences réelles. Sur notre plateforme juridique, « cette recherche n'a renvoyé aucune décision » et « nous n'avons pas pu poser la question » finissaient au même endroit, et le copilote disait à un avocat que certaines décisions n'existaient pas alors que le moteur était en réalité bloqué. Ce sont deux réponses différentes et l'utilisateur mérite de savoir laquelle.

Ce qui existe sur le marché et en quoi cela diffère

Sur Laravel la référence est spatie/crawler, un excellent paquet pour ce qu'il fait. La différence, c'est qu'il résout un autre problème.

Ce paquet est un explorateur de découverte : vous lui donnez une URL, il extrait les liens, les met en file et parcourt tout le site, en respectant robots.txt et avec un pool de requêtes concurrentes. Si vous devez parcourir un site, c'est l'outil.

Larascraper, que nous maintenons, ne découvre rien : il part de chemins déjà connus et va chercher des données précises. En échange il apporte trois choses que l'autre ne prévoit pas parce qu'il n'en a pas besoin : le calendrier partagé entre processus dont parle cet article, le blocage par sortie avec repli exponentiel, et une chaîne d'actions pour piloter la page vivante, avec clics, formulaires, conditions et boucles bornées.

Ce dernier point est celui qui tranche en pratique. Rendre du JavaScript et retourner le HTML final, les deux le font. Remplir un formulaire, résoudre un captcha textuel, soumettre et capturer le PDF que renvoie le serveur, en répétant jusqu'à huit fois si le captcha échoue, c'est un autre problème. Il est né de là : des journaux officiels avec des visionneuses PDF derrière un captcha et des documents scannés sans couche de texte.

Trois choses à mettre en place

Aucune n'est compliquée, mais mieux vaut les régler avant la première exécution en production.

Ajustez la concurrence selon le mode. Quand l'extraction n'a pas besoin de navigateur, les requêtes se chevauchent réellement. Avec navigateur, chaque exécution est un Chromium isolé et elles passent une par une. Configurer vingt exécutions parallèles avec navigateur n'accélère rien.

Il faut installer des choses hors de Composer. Node, les paquets Puppeteer et un binaire Chrome, dans l'environnement même où cela s'exécute. Si vous travaillez en conteneurs, dans le conteneur. Et pour extraire du texte de PDF il faut des utilitaires système.

Et le piège qui nous a coûté un après-midi : lancer le navigateur exige d'exécuter un processus externe, et sur beaucoup de déploiements cette fonction est désactivée par sécurité dans le PHP qui sert les requêtes web. En ligne de commande, non. La solution n'est pas de l'ouvrir sur le serveur web, c'est de déplacer l'extraction dans une file, là où elle devrait être de toute façon.

Et si vous ne travaillez pas avec Laravel

La conception se transpose entièrement, parce que le problème n'est pas celui du langage.

Il vous faut trois choses : un calendrier partagé dans un stockage visible par tous les processus, avec réservation du créneau avant l'attente ; un registre des sorties bloquées avec repli exponentiel ; et séparer l'échec de transport de l'échec de contenu pour ne pas mentir à qui consomme les données.

Si votre extraction vit dans un seul processus, rien de tout cela n'est nécessaire et un sleep suffit. Dès qu'un second processus apparaît, votre limite de débit a cessé d'exister sans que le code ait changé.

Où est le code

Le paquet est publié sur Packagist, le code sur GitHub et la documentation du paquet couvre toutes les options. C'est la couche d'extraction derrière Crowd Legal, où elle parcourt les journaux officiels de plusieurs pays.

Si vous devez intégrer des données de sources sans API, c'est l'une des choses que nous avons résolues le plus souvent. Nous la traitons en applications web sur mesure et développement Laravel, et si vous nous décrivez le cas nous vous dirons par où passer.

Écrit par
Edu Lazaro
Edu Lazaro
Founder & Lead Developer en AndorraDev

Développeur full-stack avec plus de 15 ans d'expérience en Laravel, React, Node.js et architectures cloud. J'aide les entreprises en Andorre à construire leur présence digitale.

Partner de diseño · ionospace.
Besoin d'aide? ×
Andie by AndorraDev
Assistant IA + équipe humaine
Assistant IA d'AndorraDev
Andie
Bonjour! Je suis Andie, l'assistant IA d'AndorraDev. Comment puis-je vous aider? Si vous souhaitez parler avec Edu, il suffit de le demander.
17:28