Retour au blog
Guides
Ștefan RăcilăLast updated on Apr 29, 202613 min read

Tutoriel Scrapy Splash : Rendre des pages JavaScript

Tutoriel Scrapy Splash : Rendre des pages JavaScript
En bref : Scrapy Splash associe le moteur de crawling rapide de Scrapy au navigateur sans interface graphique Splash pour afficher les pages riches en JavaScript. Ce tutoriel sur Scrapy Splash vous guide à travers la mise en place de Docker, la configuration du projet Scrapy, les bases de SplashRequest, les scripts Lua pour le défilement et les clics, l'intégration d'un proxy, ainsi que la résolution des erreurs les plus courantes que vous rencontrerez.

Scrapy est l'un des frameworks de crawling web les plus efficaces de l'écosystème Python, mais il présente un point faible bien connu : il ne peut pas exécuter de JavaScript. Tout site qui charge des données via un rendu côté client, des appels AJAX ou des frameworks d'applications monopages est invisible pour un spider Scrapy standard. C'est exactement le problème que résout ce tutoriel sur Scrapy Splash.

Scrapy Splash est une couche d'intégration entre Scrapy et le navigateur headless Splash. Splash est un service de rendu léger basé sur Qt, développé par Zyte (la même équipe à l'origine de Scrapy), qui expose une API HTTP. Au lieu d'exécuter un navigateur de bureau complet, Splash charge une page dans un moteur WebKit allégé, exécute le JavaScript et renvoie le code HTML entièrement rendu à votre spider. Vos méthodes de parsing continuent de fonctionner avec les sélecteurs CSS et XPath standard, comme si de rien n’était.

Dans ce guide, vous allez installer Docker et Splash à partir de zéro, configurer votre projet Scrapy, écrire des robots qui affichent des pages dynamiques, créer des scripts Lua pour des interactions avancées, configurer des proxys et résoudre les erreurs qui posent le plus de problèmes aux débutants.

Qu'est-ce que Scrapy Splash et quand faut-il l'utiliser ?

Splash est un navigateur sans interface graphique doté d'une API HTTP qui rend les pages web chargées via JavaScript. Contrairement aux navigateurs complets, Splash est conçu pour être léger : il se lance à l'intérieur d'un conteneur Docker, écoute sur un port et renvoie du code HTML rendu (ou des captures d'écran PNG, ou des journaux HAR) via HTTP. Scrapy Splash est la bibliothèque officielle reliant Scrapy à ce service de rendu.

Optez pour Scrapy Splash lorsque votre site cible charge du contenu critique via JavaScript ou AJAX et que vous avez besoin du pipeline intégré, du middleware et des fonctionnalités de gestion de l'exploration de Scrapy. Zyte a conçu Splash spécifiquement pour les workflows de scraping, et il s'intègre sans heurts au cycle de vie requête/réponse de Scrapy. Cela dit, Splash utilise un moteur WebKit plus ancien, son support JavaScript n'est donc pas aussi moderne que celui des alternatives basées sur Chromium. Si votre cible s'appuie sur des API de navigateur de pointe, évaluez des outils de navigateur sans interface graphique dotés d'un backend Chromium.

Pour un rendu JS simple à grande échelle (pages de produits, listes de répertoires, résultats paginés), Splash reste une option pratique et économe en ressources.

Prérequis et configuration de l'environnement

Avant de vous lancer dans ce tutoriel Scrapy Splash, assurez-vous de disposer des éléments suivants :

  • Python 3.7+ installé sur votre système
  • Un projet Scrapy (ou l'intention d'en créer un)
  • Docker Desktop (ou le moteur Docker sous Linux) pour exécuter le conteneur Splash
  • Un terminal pour exécuter les commandes Docker et Scrapy CLI

Voilà la liste complète. La section suivante traite de l'installation de Docker.

Installation de Docker et exécution du conteneur Splash

Splash s'exécute à l'intérieur d'un conteneur Docker, il faut donc d'abord installer Docker. Téléchargez Docker Desktop pour macOS ou Windows, ou installez le moteur Docker directement sur Linux. Une fois Docker lancé, récupérez l'image officielle de Splash :

docker pull scrapinghub/splash

Lancez ensuite le conteneur :

docker run -it -p 8050:8050 --rm scrapinghub/splash

Cela mappe le port 8050 du conteneur au port 8050 de votre hôte. Le --rm supprime le conteneur lorsque vous l'arrêtez.

Ouvrez http://localhost:8050/ dans votre navigateur. Si vous voyez la page interactive Splash par défaut, le service est opérationnel. Testez une URL à cet endroit pour vérifier que le rendu fonctionne avant d'écrire du code Scrapy.

Pour une configuration Docker de Splash avec Scrapy destinée à la production, tenez compte des limites de ressources. Le --max-timeout drapeau vous permet d'augmenter le délai d'expiration par défaut de 60 secondes (le maximum est d'environ 90 secondes à moins que vous ne le redéfinissiez, mais vous devriez vérifier la valeur exacte dans la documentation actuelle de Splash car les détails peuvent varier). Limitez la mémoire avec le drapeau --memory pour empêcher les pages incontrôlées de saturer votre hôte.

Configuration de votre projet Scrapy pour Splash

Si vous n'avez pas encore de projet, créez-en un :

scrapy startproject myproject

Installez le plugin scrapy-splash :

pip install scrapy scrapy-splash

Ouvrez settings.py et ajoutez ces entrées :

SPLASH_URL = 'http://localhost:8050'

DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}

SPIDER_MIDDLEWARES = {
    'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}

DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

SPLASH_URL indique à scrapy-splash où se trouve le service de rendu. SplashCookiesMiddleware gère le transfert des cookies entre Scrapy et Splash. SplashMiddleware intercepte les objets SplashRequest et les achemine via l'API HTTP de Splash. Le DUPEFILTER_CLASS garantit que le filtre de requêtes en double de Scrapy prend en compte les arguments spécifiques à Splash, empêchant ainsi le filtrage accidentel de requêtes qui ne diffèrent que par leurs paramètres de rendu.

Une fois ces paramètres configurés, votre projet est prêt pour tout spider de tutoriel Scrapy Splash que vous créerez par la suite.

Votre premier spider du tutoriel Scrapy Splash : SplashRequest en action

Générez un squelette de spider :

scrapy genspider quotes_js quotes.toscrape.com

Remplacez le modèle par défaut start_urls par start_requests, car la classe de requête par défaut de Scrapy ne passe pas par Splash :

import scrapy
from scrapy_splash import SplashRequest

class QuotesJsSpider(scrapy.Spider):
    name = 'quotes_js'

    def start_requests(self):
        yield SplashRequest(
            url='http://quotes.toscrape.com/js/',
            callback=self.parse,
            args={'wait': 2}
        )

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').get(),
                'author': quote.css('small.author::text').get(),
            }

La principale différence par rapport à une scrapy.Request est que SplashRequest envoie d'abord l'URL à Splash. Splash affiche la page, attend le nombre de secondes spécifié pour que JavaScript s'exécute, puis renvoie le code HTML entièrement affiché. À l'intérieur de parse, vous travaillez avec la réponse exactement comme vous le feriez normalement : sélecteurs CSS, XPath, tout fonctionne car les réponses Splash contiennent toutes les propriétés de réponse standard.

Exécutez-le avec scrapy crawl quotes_js et vous devriez voir les données de citation rendues dans votre sortie.

Contrôler le rendu de la page avec les arguments de SplashRequest

SplashRequest accepte plusieurs arguments qui contrôlent la manière dont Splash affiche la page :

Argument

Type

Objectif

wait

float

Nombre de secondes à attendre après le chargement de la page avant de renvoyer le code HTML

timeout

float

Temps de rendu maximal (en secondes). Valeur par défaut : 60, plafonné à environ 90 sauf si redéfini

images

int (0/1)

Définir sur 0 pour désactiver le chargement des images, ce qui accélère le rendu

resource_timeout

float

Délai d'expiration par ressource individuelle (fichier CSS, JS, image)

http_method

chaîne

Utiliser POST pour les soumissions de formulaire

body

chaîne

Contenu du corps POST, associé à http_method='POST'

Par exemple, pour envoyer une requête POST (utile pour les soumissions de formulaire) :

yield SplashRequest(
    url='https://example.com/search',
    args={
        'wait': 1,
        'http_method': 'POST',
        'body': 'query=scrapy+splash',
    },
    callback=self.parse_results,
)

Le http_method et body sont pratiques pour les sites qui traitent des formulaires de recherche ou des actions de connexion côté serveur. Cela couvre les bases du rendu JavaScript de Scrapy, mais pour l'interaction avec les pages (clics, défilement, attente d'éléments dynamiques), vous avez besoin de scripts Lua.

Écrire des scripts Lua pour des interactions avancées

L' render.html point de terminaison gère les cas simples, mais dès que vous devez interagir avec une page, vous passez au execute point de terminaison avec un script Lua. Un script Lua Scrapy Splash vous offre un contrôle étape par étape sur le navigateur :

function main(splash, args)
  splash:go(args.url)
  splash:wait(1)
  return splash:html()
end

Envoyez-le via SplashRequest en utilisant endpoint='execute' et en transmettant le script args={'lua_source': script}. À partir de là, ajoutez des attentes d'éléments, des boucles de défilement et des actions de clic.

Attendre le chargement d'éléments spécifiques

Une wait fonctionne lorsque vous connaissez le temps de chargement de la page, mais cette méthode est fragile. Interrogez plutôt un élément DOM spécifique :

function main(splash, args)
  splash:go(args.url)
  while not splash:select('.target-element') do
    splash:wait(0.5)
  end
  splash:wait(0.5)
  return splash:html()
end

Ce script boucle jusqu'à ce que splash:select() trouve un élément correspondant .target-element, en attendant une demi-seconde entre chaque tentative. Une fois l'élément apparu, une dernière brève attente gère le rendu restant. Ce modèle est bien plus fiable que d'estimer un délai statique.

Faire défiler des pages à défilement infini

Splash ne dispose pas de commandes de défilement intégrées. À la place, injectez du JavaScript pour manipuler la position de défilement. Voici un script Lua pour le défilement infini de Scrapy Splash :

function main(splash, args)
  splash:go(args.url)
  splash:wait(2)
  local scroll_count = 5
  for i = 1, scroll_count do
    splash:runjs("window.scrollTo(0, document.body.scrollHeight)")
    splash:wait(2)
  end
  return splash:html()
end

Le script défile jusqu'en bas, attend le nouveau contenu, puis recommence. Ajustez scroll_count et la durée d'attente en fonction du site. Comparez document.body.scrollHeight avant et après chaque défilement pour détecter quand aucun nouveau contenu n'apparaît.

Cliquer sur des boutons et naviguer entre les pages

Les boutons « Charger plus » et les liens de pagination nécessitent une interaction avec la souris. Utilisez splash:select() pour trouver l'élément et déclencher un clic :

function main(splash, args)
  splash:go(args.url)
  splash:wait(2)
  local btn = splash:select('.load-more-btn')
  if btn then
    btn:mouse_click()
    splash:wait(2)
  end
  return splash:html()
end

Enveloppez cela dans une boucle pour les pages comportant plusieurs déclencheurs. Pour la pagination, sélectionnez le lien « Suivant », cliquez dessus, attendez que la nouvelle page s'affiche et récupérez le code HTML à chaque étape.

Exécution de JavaScript personnalisé dans Splash

Parfois, vous n'avez pas besoin d'un workflow Lua complet. Splash vous permet d'injecter du JavaScript arbitraire à l'aide de deux méthodes : splash:evaljs() (renvoie une valeur) et splash:runjs() (s'exécute sans renvoyer de valeur).

function main(splash, args)
  splash:go(args.url)
  splash:wait(1)
  local title = splash:evaljs("document.title")
  splash:runjs("document.querySelector('.popup-close').click()")
  splash:wait(0.5)
  return {html = splash:html(), title = title}
end

Cela est utile pour fermer les bannières de cookies, fermer les fenêtres modales ou extraire une valeur calculée avant de récupérer le code HTML de la page. Vous pouvez également passer du code JavaScript via le js_source paramètre d'une SplashRequest standard (sans Lua), ce qui exécute le JS après le chargement de la page mais avant la capture de l'instantané HTML.

Utilisation de proxys avec Scrapy Splash

La rotation de votre adresse IP permet d'éviter les blocages lors de toute opération de scraping d'envergure. Acheminez les requêtes via un proxy Scrapy Splash en transmettant les détails dans les arguments de SplashRequest :

yield SplashRequest(
    url='https://example.com',
    callback=self.parse,
    args={
        'wait': 2,
        'proxy': 'http://user:pass@proxyhost:port',
    },
)

Vous pouvez également configurer le proxy à l'intérieur d'un script Lua à l'aide de splash:on_request():

function main(splash, args)
  splash:on_request(function(request)
    request:set_proxy{
      host = "proxyhost",
      port = 8080,
      username = "user",
      password = "pass",
    }
  end)
  splash:go(args.url)
  splash:wait(2)
  return splash:html()
end

L'approche Lua vous permet d'appliquer différents proxys à différentes sous-requêtes au sein d'un même chargement de page. Gardez à l'esprit que Splash ne contourne pas les systèmes anti-bot ; il se contente d'afficher la page. Vous avez toujours besoin de proxys résidentiels ou de centre de données correctement rotatifs pour éviter les blocages au niveau de l'adresse IP.

Erreurs courantes et dépannage

C'est là que la plupart des tutoriels sur Splash vous laissent en plan. Voici les erreurs que vous rencontrerez le plus souvent :

Connexion refusée sur localhost:8050. Le conteneur Docker Splash n'est pas en cours d'exécution. Vérifiez avec docker ps. S'il est en cours d'exécution mais inaccessible, vérifiez que le port 8050 n'est pas bloqué par votre pare-feu ou occupé par un autre processus.

504 Timeout de la passerelle. Le rendu de la page a pris plus de temps que le délai d'attente autorisé. Augmentez l' timeout argument dans votre SplashRequest. La limite par défaut est d'environ 90 secondes. Pour des rendus plus longs, démarrez le conteneur avec une --max-timeout (vérifiez la documentation Splash actuelle, car les détails peuvent varier d'une version à l'autre).

Erreurs de script Lua (« bad argument », « attempt to index a nil value »). Elles signifient généralement que splash:select() renvoyées nil car l'élément ne figurait pas encore dans le DOM. Ajoutez une boucle d'attente ou d'interrogation avant d'interagir avec lui.

Conteneur Docker arrêté (OOM). Splash peut consommer beaucoup de mémoire sur les pages lourdes. Définissez les limites de mémoire de Docker avec --memory 2g et désactivez le chargement des images (images=0). Pour plusieurs instances, utilisez Docker Compose avec des contraintes de ressources par conteneur.

Code HTML vide ou incomplet renvoyé. Le JavaScript de la page a peut-être besoin de plus de temps. Augmentez wait. Si les ressources tierces sont lentes, définissez resource_timeout pour les ignorer.

Scrapy Splash vs Scrapy-Playwright vs Selenium

Le choix de l'outil de rendu approprié dépend de votre projet. Voici une comparaison des trois options les plus courantes parmi les alternatives à Scrapy Splash :

Fonctionnalité

Scrapy Splash

Scrapy-Playwright

Selenium

Moteur de navigation

WebKit (basé sur Qt)

Chromium, Firefox, WebKit

Chrome, Firefox, Edge

Intégration de Scrapy

Native (scrapy-splash)

Native (scrapy-playwright)

Nécessite un middleware personnalisé

Prise en charge asynchrone

Limité (API HTTP)

Asynchrone complet (basé sur Playwright)

Synchrone par défaut

Utilisation des ressources

Faible à modéré

Modérée

Élevé

Prise en charge de JS moderne

Partiel (WebKit ancien)

Complet (Chromium)

Complet

Contournement anti-bot

Aucun intégré

Aucun intégré

Aucun intégré

Idéal pour

Rendu JS léger à grande échelle

SPA complexes, sites JS modernes

Projets non Scrapy, tests

Splash est le choix idéal lorsque vous souhaitez un overhead minimal et que vos pages cibles ne s'appuient pas sur des API de navigateur de pointe. Pour les applications monopages modernes, Scrapy-Playwright, avec son backend Chromium, est probablement plus adapté. Selenium fonctionne, mais ne dispose pas d'une intégration native avec Scrapy. Aucun de ces outils ne gère seul la protection anti-bot, vous aurez donc toujours besoin d'une couche proxy pour le scraping en production. Utilisez ce tutoriel Scrapy Splash comme base, et explorez d'autres alternatives lorsque le projet l'exige.

Points clés

  • Splash s'exécute dans Docker et se connecte à Scrapy via une API HTTP. Une fois que le conteneur est sur le port 8050 et settings.py est configuré, vos robots peuvent afficher des pages JavaScript à l'aide d'un simple appel SplashRequest.
  • Utilisez des scripts Lua lorsque vous avez besoin d'interaction. Les délais d'attente fixes couvrent les cas simples, mais l'interrogation d'éléments, les boucles de défilement et les actions de clic nécessitent l' execute point de terminaison avec un script Lua.
  • Les proxys sont indispensables pour le scraping en production. Splash affiche les pages mais ne contourne pas les protections anti-bot. Acheminez les requêtes via des proxys tournants à l'aide des arguments SplashRequest ou splash:on_request() en Lua.
  • Splash est léger mais vieillissant. Il s'intègre parfaitement à Scrapy, mais son moteur WebKit ne prend pas en charge certaines API JavaScript modernes. Évaluez Scrapy-Playwright pour les sites nécessitant un backend Chromium.
  • Effectuez un dépannage systématique. La plupart des problèmes liés à Splash se résument à des délais d'attente, des éléments manquants ou des limites de ressources Docker.

FAQ

Scrapy Splash peut-il gérer les applications monopages construites avec React ou Vue ?

Il peut afficher de nombreuses applications React et Vue, mais les résultats dépendent des API JavaScript utilisées par l'application. Splash fonctionne sur un moteur WebKit plus ancien, donc les applications qui s'appuient sur des fonctionnalités modernes des navigateurs (comme IntersectionObserver ou la syntaxe ES2020+) peuvent ne pas s'afficher correctement. Testez votre URL cible dans l'interface Web de Splash à l'adresse localhost:8050 avant de créer un spider complet.

De combien de mémoire un conteneur Docker Splash a-t-il besoin pour le scraping en production ?

Prévoyez au moins 1 à 2 Go par instance pour des charges de travail typiques. Les pages contenant des images lourdes ou du JavaScript complexe peuvent augmenter la consommation de mémoire. Désactivez le chargement des images avec images=0 pour réduire la consommation, et définissez le drapeau --memory pour empêcher qu'un seul conteneur n'épuise les ressources de l'hôte.

Scrapy Splash est-il toujours maintenu, et quelles sont les alternatives disponibles ?

Splash ne bénéficie plus que de mises à jour sporadiques et son développement de fonctionnalités n'est plus actif. Il fonctionne toujours pour de nombreux cas d'utilisation, mais la communauté s'est largement tournée vers Scrapy-Playwright pour les nouveaux projets. Selenium reste une option en dehors de l'écosystème Scrapy. Chaque outil présente des compromis en matière de prise en charge des moteurs de navigateur, de capacités asynchrones et d'utilisation des ressources.

Comment transmettre des cookies ou des en-têtes personnalisés via SplashRequest ?

Ajoutez une cookies clé dans le args , ou définissez les en-têtes à l'aide de l' headers . Dans un script Lua, utilisez splash:set_custom_headers() avant d'appeler splash:go(). Les cookies provenant du cookie jar de Scrapy sont automatiquement transférés lorsque SplashCookiesMiddleware est activé dans vos paramètres.

Conclusion

Ce tutoriel sur Splash pour Scrapy vous a guidé à travers le flux de travail complet : mise en place d'un conteneur Splash, configuration de votre projet Scrapy, écriture de robots avec SplashRequest, création de scripts Lua pour le défilement et les clics, et configuration des proxys. Les techniques de dépannage abordées ici devraient vous faire gagner des heures de débogage.

Splash gère bien les tâches de rendu simples, mais il s'agit désormais d'une technologie plus ancienne. Si vos cibles repoussent les limites du JavaScript moderne, évaluez les alternatives basées sur Chromium. Quel que soit l'outil de rendu que vous choisissez, le véritable goulot d'étranglement dans le scraping en production est rarement le navigateur ; il s'agit plutôt de contourner les défenses anti-bot et de gérer l'infrastructure de proxys à grande échelle.

Si vous préférez éviter complètement les casse-tête liés à l'infrastructure, WebScrapingAPI gère la rotation des proxys, la résolution des CAPTCHA et le rendu JavaScript derrière un seul point de terminaison API, afin que vous puissiez vous concentrer sur l'analyse des données plutôt que sur la gestion de l'infrastructure.

À propos de l'auteur
Ștefan Răcilă, Développeur Full Stack @ WebScrapingAPI
Ștefan RăcilăDéveloppeur Full Stack

Stefan Racila est ingénieur DevOps et Full Stack chez WebScrapingAPI ; il développe des fonctionnalités pour les produits et assure la maintenance de l'infrastructure qui garantit la fiabilité de la plateforme.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.