Scraping

Comment gérer le défilement infini et la pagination dynamique lors du scraping

Impossible de rejouer le endpoint caché ? Gérez le défilement infini directement dans le rendu avec des chaînes de scroll, des clics sur load-more et des vérifications pour les listes virtualisées.

Chris Collins

Chris Collins

15 septembre 2026 · 11 min de lecture

La meilleure façon de scraper un flux à défilement infini n’est généralement pas de faire défiler du tout. Derrière la plupart des flux se trouve une requête paginée, souvent basée sur un curseur, et rejouer cette requête directement est plus rapide, moins coûteux et plus fiable que de piloter un navigateur. Cette approche est détaillée dans comment scraper la pagination et le défilement infini de façon fiable.

Ce guide traite des cas où cela ne fonctionne pas. La requête est signée avec un jeton de courte durée. La réponse est un bloc opaque que la page décode côté client. Le flux ne se charge que lorsqu’un élément entre dans la fenêtre d’affichage. Le bouton « page suivante » est relié à un état que vous ne pouvez pas reconstruire. Dans ces cas-là, vous devez gérer le défilement et les clics à l’intérieur d’un rendu réel, et il existe une poignée de façons pour que cela échoue.

Les exemples utilisent la Shifter Web Scraping API, qui exécute la page dans Chrome headless et accepte une chaîne d’actions navigateur avant la capture.

Quatre types de pagination dynamique

Identifiez de quel type il s’agit avant d’écrire quoi que ce soit, car chacun nécessite une technique différente.

MotifCe qui déclenche le lot suivantTechnique
Flux déclenché au défilementUn élément proche du bas entrant dans la fenêtre d’affichageDéfiler jusqu’à une sentinelle, attendre, répéter
Bouton « charger plus »Un clic sur un bouton qui ajoute des élémentsCliquer, attendre les nouveaux éléments, répéter
Pagination par état d’URLLa page met à jour l’URL au fur et à mesure que l’on avance dans les résultatsRécupérer ces URLs directement, sans défilement
Liste virtualiséeDéfilement, mais les anciennes lignes sont retirées du DOM au fur et à mesure que de nouvelles apparaissentCapturer par fenêtres, ou utiliser la requête sous-jacente

Le troisième cas mérite d’être vérifié en premier car c’est le moins coûteux à gérer. Faites défiler un flux dans un navigateur normal et observez la barre d’adresse. Si un paramètre de page ou d’offset change, le site vous a déjà fourni une URL paginée, et chaque page est une requête ordinaire.

Le quatrième est celui qui fait perdre des données silencieusement, et il fait l’objet d’une section dédiée ci-dessous.

Rendu et attente correcte

Chaque technique dynamique repose sur les deux mêmes contrôles. render_js=1 exécute la page dans Chrome headless, au même coût d’un crédit par requête réussie qu’une récupération statique. wait_for_css retient la capture jusqu’à ce qu’un sélecteur existe dans le DOM, afin de ne jamais extraire d’une page qui n’a pas encore été peuplée. Les contrôles de rendu sont documentés dans le rendu JavaScript.

Choisissez le sélecteur d’attente avec soin. Attendre le conteneur de la liste ne suffit pas, car de nombreux frameworks rendent d’abord un conteneur vide avant de le remplir. Attendez plutôt le premier élément de la liste.

Flux déclenchés au défilement : défiler jusqu’à une sentinelle

Les actions du navigateur figurent dans js_instructions, un tableau JSON d’étapes exécutées dans l’ordre avant la capture. Les actions documentées sont scrollTo, click et wait.

Le motif pour un flux déclenché au défilement consiste à défiler jusqu’à un élément situé après la liste, attendre le chargement du lot suivant, et répéter. Comme la liste s’agrandit, cet élément se déplace plus bas à chaque fois, donc défiler à nouveau jusqu’à lui déclenche le chargement suivant.

import json
import os

import requests

API = "https://scrape.shifter.io/v1"

instructions = [
    {"action": "click", "selector": "button.accept-cookies"},
    {"action": "scrollTo", "selector": "footer", "timeout": 5000, "block": "start"},
    {"action": "wait", "duration": 2000},
    {"action": "scrollTo", "selector": "footer", "timeout": 5000, "block": "start"},
    {"action": "wait", "duration": 2000},
    {"action": "scrollTo", "selector": "footer", "timeout": 5000, "block": "start"},
    {"action": "wait", "duration": 2000},
]

rules = {
    "items": {
        "selector": "article.card",
        "type": "list",
        "item": {
            "link": {"selector": "a.card-link", "output": "@href"},
            "name": {"selector": "h3", "output": "text"},
            "price": {"selector": ".price", "output": "text"},
        },
    }
}

params = {
    "api_key": os.environ["SHIFTER_API_KEY"],
    "url": "https://shop.example.com/category/shoes",
    "render_js": 1,
    "wait_for_css": "article.card",
    "js_instructions": json.dumps(instructions),
    "extract_rules": json.dumps(rules),
}

resp = requests.get(API, params=params, timeout=120)
resp.raise_for_status()
items = resp.json()["items"]

Plusieurs détails dans ce code sont délibérés.

La bannière de cookies est écartée en premier, car un overlay peut intercepter le défilement ou le clic. L’attente entre les défilements laisse à la requête réseau le temps de se terminer et au DOM le temps de se mettre à jour ; trop courte, vous capturez avant l’arrivée du lot. extract_rules avec un type liste renvoie chaque carte comme un objet JSON, donc il n’y a pas de parseur de votre côté, et requests encode en URL les deux paramètres JSON pour vous. La syntaxe d’extraction se trouve dans les règles d’extraction.

Testez la chaîne sur une page d’exemple avant de la lancer à grande échelle, et ajustez le nombre d’étapes de défilement et la durée d’attente en fonction de la vitesse réelle de chargement de ce site.

Boutons « charger plus » : cliquer, puis attendre la croissance

Un bouton « charger plus » suit la même boucle avec un déclencheur différent :

[
  {"action": "click", "selector": "button.load-more", "timeout": 3000},
  {"action": "wait", "duration": 2000},
  {"action": "click", "selector": "button.load-more", "timeout": 3000},
  {"action": "wait", "duration": 2000}
]

Deux choses piègent les gens. Le sélecteur du bouton change souvent d’état pendant le chargement, gagnant une classe désactivée ou un spinner, si bien que le second clic peut se déclencher avant que le bouton ne redevienne cliquable, à moins que l’attente ne soit assez longue. Et le bouton disparaît généralement lorsqu’il n’y a plus rien à charger, ce qui constitue un signal utile de complétude mais signifie que votre chaîne doit tolérer que les derniers clics n’aient rien sur quoi agir. Là encore, testez sur le site réel.

Le budget temps d’un seul rendu

Tout ce qui précède se déroule dans une seule session navigateur limitée dans le temps. wait_for_css expire au bout de 30 secondes par défaut, et timeout plafonne le temps que le navigateur peut passer sur la page. Un flux comportant des milliers d’éléments ne finira pas de se charger dans un seul rendu, quel que soit le nombre d’étapes de défilement enchaînées.

Il faut donc scinder le problème plutôt qu’allonger la chaîne.

Restreignez l’ensemble de résultats. Les filtres, les ordres de tri et les facettes de catégorie produisent généralement des flux plus petits. Vingt flux restreints qui se chargent chacun entièrement sont plus fiables qu’un seul flux immense qui ne se termine jamais.

Paginez à travers des URLs filtrées. De nombreux sites combinent un filtre avec un état d’URL, ce qui transforme un défilement illimité en un ensemble fini de requêtes ordinaires.

Rabattez-vous sur la requête sous-jacente lorsque le flux est véritablement long et ne peut pas être restreint. Récupérez-la sans rendu ; pour les points d’accès qui renvoient du JSON, auto_parser=1 renvoie le corps analysé.

Listes virtualisées : la perte de données silencieuse

Certains flux, en particulier les très longs, utilisent la virtualisation de liste. Seules les lignes proches de la fenêtre d’affichage existent dans le DOM. À mesure que l’on défile vers le bas, les lignes du haut sont retirées.

Faites défiler une liste virtualisée jusqu’en bas et capturez, et vous obtenez le dernier écran d’éléments, pas tous les éléments que vous avez fait défiler. Rien ne génère d’erreur. L’extraction renvoie une liste propre, plausible, et incomplète.

Détectez cela avant de faire confiance à un résultat. Exécutez la chaîne de défilement, puis vérifiez si les éléments du premier écran sont encore présents dans le résultat capturé. S’ils ont disparu, la liste est virtualisée, et défiler puis capturer ne peut pas fonctionner. Utilisez plutôt la pagination par état d’URL ou la requête sous-jacente.

Pagination dynamique à travers plusieurs requêtes

Lorsque chaque page est une requête séparée qui dépend d’un état côté serveur, comme un curseur stocké contre votre session, maintenez cet état constant tout au long du parcours avec session_id. Une session conserve les cookies, l’état du navigateur et l’IP en amont à travers les requêtes, et expire au bout de 10 minutes d’inactivité. Conservez le même country pendant toute la durée d’une session, car en changer en cours de parcours peut invalider des cookies liés à la locale. Les détails se trouvent dans sessions et proxies.

params.update({
    "session_id": "shoes-walk-07",
    "country": "de",
})

Maintenez un parcours en mouvement. Une session qui reste inactive pendant que votre code fait quelque chose de lent entre les pages expirera et cassera le curseur.

S’assurer d’avoir tout récupéré

Un scraper qui s’arrête trop tôt ressemble exactement à un scraper qui a terminé. Intégrez des vérifications de complétude dans chaque exécution.

  • Comparez au total affiché. De nombreux flux affichent un nombre de résultats. Si la page indique 1 284 et que vous en avez extrait 960, vous vous êtes arrêté trop tôt.
  • Dédupliquez sur une clé stable, comme le lien ou l’ID de l’élément, jamais sur la position. Les flux à défilement re-rendent régulièrement les éléments, et la position se décale à mesure que du contenu est inséré.
  • Vérifiez le marqueur de fin. Un bouton « charger plus » disparu ou un message de fin de résultats est une confirmation positive. Son absence après votre dernière étape signifie que la chaîne était trop courte.
  • Suivez la complétude dans le temps. Une catégorie qui a fourni 1 200 éléments hier et 400 aujourd’hui a généralement changé de balisage ou de comportement de chargement, pas d’inventaire.

Crédits et latence : choisir l’approche par site

ApprocheCréditsLatenceRisque de fiabilité
Rejouer la requête sous-jacenteAucun si envoyée directement ; un par page via l’APIFaibleRequêtes signées ou opaques
Pagination par état d’URLUn par pageFaible à modéréeNécessite une URL paginée
Chaîne de défilement ou de clics dans un seul renduUn pour toute la chaîneÉlevéeBudget temps, virtualisation

Un rendu qui défile à travers plusieurs lots coûte un crédit, alors que rejouer les mêmes lots comme des requêtes séparées coûte un crédit chacune. Le compromis porte sur la latence et le budget temps : les chaînes longues sont plus lentes et plus susceptibles d’expirer. Utilisez les chaînes de défilement pour les flux modérés où la requête sous-jacente est impraticable, et privilégiez les deux autres approches pour tout le reste.

FAQ

Dois-je toujours rendre le JavaScript pour le défilement infini ?

Non. Vérifiez d’abord la pagination par état d’URL et la possibilité de rejouer la requête sous-jacente. Le rendu est le repli, pas le comportement par défaut.

Combien d’étapes de défilement la chaîne doit-elle comporter ?

Autant que le site en a besoin pour charger les lots souhaités dans le budget temps imparti. Mesurez sur une page d’exemple, et si vous avez besoin de plus que ce que le budget permet, restreignez plutôt le flux.

Pourquoi mon extraction renvoie-t-elle moins d’éléments que ceux que j’ai fait défiler ?

Presque toujours à cause de la virtualisation de liste, où les lignes quittent le DOM au fur et à mesure du défilement. Vérifiez si les éléments du premier écran survivent jusqu’à la capture.

Chaque étape de défilement coûte-t-elle un crédit ?

Non. Toute la chaîne s’exécute dans une seule requête, et une requête réussie coûte un crédit.

En résumé

Le défilement infini est une pagination par curseur avec un navigateur devant, et lorsque vous pouvez atteindre le curseur directement, vous devriez le faire. Lorsque ce n’est pas possible, gérez-le à l’intérieur du rendu : attendez le premier élément plutôt que le conteneur, défilez jusqu’à une sentinelle ou cliquez sur « charger plus » avec suffisamment d’attente entre les étapes, respectez le budget temps en restreignant le flux, testez la virtualisation avant de faire confiance à une capture, et vérifiez la complétude par rapport aux totaux propres du site.

Pour décider quand une requête API rendue est le bon outil dès le départ, voir quand vous avez besoin d’une web scraping API pour les sites riches en JavaScript. Le produit se trouve sur la page web scraping API avec rendu JS, avec les formules sur la page de tarification.

Prêt à commencer ?

Essayez les proxies résidentiels de Shifter, 205M+ IPs, 195+ pays, à partir de 0,75 $/GB.

Commencer