Il y a deux semaines je publiais un article intitulé "Comment crawler une page web en NodeJS avec Puppeteer et Cheerio" dans lequel je fournissais un snippet de crawler basique.

Si vous n'êtes pas encore familier avec le crawling de site web je vous recommande de le lire avant de continuer cet article !

Comme vous l'aurez compris, la méthode pour récupérer les informations d'un site web lambda est très simple, mais si vous désirez pouvoir parcourir de plus gros sites (ou tout simplement un plus large panel), vous risquez de vous confrontez à quelques difficultés techniques.

Pour ma part, lorsque j'ai monté mon projet Kalico en 2018, j'avais pour objectif de récupérer les prix de nombreux articles sur différentes plateformes e-commerces, dont les géants Amazon et eBay.

Le problème

Pour un site e-commerce (notamment), il est normal de tout faire pour empêcher des scripts extérieurs de récupérer automatiquement une grosse quantité d'information.

A l'heure où nous sommes, les données sont une part non-négligeable de la valeur des entreprise, notamment pour les géants du numérique.

C'est pour cette raison que la plupart de ces sociétés mettent en oeuvre des moyens techniques afin d'empêcher au maximum cette collecte. Le problème étant qu'en mettant en place des filtres trop stricts, ces plateformes pourraient risquer de bloquer de réels utilisateurs par mégarde, ce qu'elles ne peuvent pas se permettre.

Chaque plateforme exploitera donc différentes données et meta-données laissées par un "visiteur" pour essayer de détecter si oui, ou non, il s'agit d'une personne réelle ou d'un script.

De notre côté, notre but sera de jouer au jeu du chat et de la souris, en trouvant des techniques comme celles ci-dessous qui nous permettront de passer pour un utilisateur lambda la plupart du temps.

Les solutions techniques

1 - Faire une rotation du user-agent

Le user-agent est une meta-donnée présente dans l'en-tête d'une requête http qui donne des informations sur le navigateur et sa version, le matériel, son système d'exploitation, etc...

Par exemple, mon user-agent actuel est le suivant : Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0

Certains sites vont donc faire un recoupement de plusieurs méta-données comme celle-ci (en plus de l'IP) afin de détecter si un utilisateur est à l'origine d'un trop grand nombre de requêtes par exemple.

C'est notamment ce qu'on appelle le "fingerprinting", que j'évoquais dans un précédent article.

Pour contrer cette pratique, la première méthode est très simple et consiste à injecter à chaque nouvelle requête un user-agent différent pris dans une liste prédéfinie ou généré aléatoirement à la demande.

Exemple :

"Mozilla/5.0 (iPhone; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1"
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_0_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13A404 Safari/601.1"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"
"Mozilla/5.0 (Linux; U; Android 2.2) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-en) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"

2 - Avoir plusieurs proxies

Evidemment, même si les user-agents sont différents, il est très facile pour un site de bloquer une adresse lorsqu'il détecte un nombre de requêtes trop important provennant d'une seule IP.

Il est donc préférable d'avoir une liste de proxies que l'on va affecter à différentes instances du headless-browser (puppeteer) afin d'éviter que l'adresse du serveur soit détectée en quelques minutes.

Les proxies peuvent provenir de listes gratuites sur le net, ou bien vous pouvez aussi en monter vous même grâce à des instances clouds par exemple, ce qui vous permet notamment d'avoir de meilleurs performances.

Point fort supplémentaire des proxies : il est possible d'utiliser des localisations différentes pour les serveurs (comme différents pays), c'est ce que j'ai fait pour faciliter la récupération des prix dans les bonnes devises sur Amazon !

3 - Délais de clics aléatoires

Une fois que toutes les informations du crawler ont été "anonymisées" en amont du chargement de la page, il est beaucoup plus compliqué pour un site de détecter un robot.

Mais certains sites utilisent une analyse du comportement de l'utilisateur afin de détecter si ce dernier agit "comme un humain" ou non.

Par exemple, un visiteur qui clique sur 30 liens en 5 secondes devient suspect car humainement infaisable, mais le temps entre chaque clic est aussi important !

Car oui, si vous demandez à votre script d'attendre 5 secondes entre chaque clic, le site pourra détecter que ces actions ont été automatisées, la solution est donc d'ajouter un délai aléatoire (compris par exemple entre 5 et 20 secondes) pour chaque clic.

4 - Requêter le HTML de différentes manières

Certains sites ne misent pas sur la détection de comportement ou le fingerprinting, mais concentrent plutôt leurs efforts pour rendre le contenu de la page difficilement récupérable.

On peut par exemple tomber sur des sites qui générent leurs ids et classes d'éléments à la volée à chaque nouvelle requête, rendant le processus de sélection d'un élément très compliqué.

L'une des solutions est d'analyser en amont ce que peut nous renvoyer le site et de préparer plusieurs requêtes (avec Cheerio par exemple) pour tester différents layouts de pages.

Il est même parfois possible de tomber sur des sites presque impossible à parcourir automatiquement !

5 - Eviter les honeypots

La technique du Honeypot (ou pot de miel) consiste à laisser un élément non sécurisé sur lequel un utilisateur lambda n'irait jamais mais qui servira à piéger un robot.

Pour les crawlers, cela consiste en un lien invisible sur la page, mais qui bloquera instantannément le crawler si ce dernier clique dessus. Pour éviter ces honeypots, on peut :

  • Eviter les liens cachés par du css (opacity:0, display: none ou visibility: hidden)
  • Eviter les liens vides (sans aucun contenu)
  • Eviter les liens contenant une image de 1px sur 1px

Les honeypots sont difficiles à éviter mais si vous mettez en place les 4 techniques précédentes vous aurez déjà évité 99% des problèmes de vos crawlers !

J'espère que cet article vous aura plu, et à bientôt sur le blog !

À propos de l'auteur

Hello, je suis Nicolas Brondin-Bernard, ingénieur web indépendant depuis 2015 passionné par le partage de d'expériences et de connaissances.

Aujourd'hui je suis aussi coach pour développeurs web juniors, tu peux me contacter sur nicolas@brondin.com, sur mon site ou devenir membre de ma newsletter pour ne jamais louper le meilleur article de la semaine et être tenu au courant de mes projets !


Photo by Lenin Estrada on Unsplash