← Tous les articles

Le Plafond des 10 000 Résultats : Comment Scraper au-delà des Limites de Pagination

Vous construisez un scraper pour une page de résultats de recherche. Il fonctionne. Vous paginez : page 1, page 2, page 10, page 40. De nouveaux éléments continuent d’arriver. Puis, quelque part vers la page 65, les résultats se tarissent. La page se charge encore. La mise en page est intacte. Pas de CAPTCHA, pas de message de blocage. Il n’y a tout simplement plus d’annonces, alors que l’en-tête du site affiche « plus de 48 000 résultats ».

Vous n’avez pas été banni. Vous avez heurté un plafond de résultats. Presque toutes les grandes interfaces de recherche en ont un, et si vous ne l’anticipez pas, votre jeu de données s’arrête silencieusement à une fraction de ce qui existe réellement. Cet article explique ce qu’est ce plafond, pourquoi il existe, comment le distinguer d’un blocage, et l’unique technique fiable pour collecter malgré tout l’ensemble complet : le partitionnement de l’espace de requête.

À quoi ressemble le plafond de résultats

Le symptôme est précis. Les pages peu profondes renvoient des données fraîches. Les pages profondes renvoient une grille vide, ou répètent la même dernière page, ou redirigent vers la page 1. Et surtout : la réponse est une page valide. Code de statut 200. Titre correct. Navigation correcte. Simplement sans éléments.

Deux nombres définissent généralement le plafond :

  • Un décalage (offset) maximal, souvent 10 000. Beaucoup de sites paginent avec un offset ou un numéro de page qui correspond à page * taille_de_page. Dès que l’offset dépasse ~10 000, le backend refuse d’aller plus loin.
  • Un maximum de résultats par requête, qui est la même limite vue de l’autre côté. Avec une taille de page de 240, vous obtenez environ 10 000 / 240 = 41 pages utiles. Avec 50, vous obtenez 200 pages. Dans les deux cas, vous plafonnez près de 10 000 éléments.

Vous verrez cela sur les grandes places de marché e-commerce, les sites d’emploi, les portails immobiliers et les moteurs de recherche généralistes. Ce n’est pas un bug. C’est un plafond délibéré.

Pourquoi les sites limitent la pagination profonde

Il n’y a aucun complot ici. Le plafond est une décision de performance, et le comprendre vous montre comment le contourner.

La plupart des backends de recherche utilisent la pagination par décalage (offset). Pour servir la page 400, la base de données doit parcourir les 399 premières pages de résultats puis renvoyer la tranche suivante. Ce travail de « parcours » croît linéairement avec la profondeur. La page 1 est bon marché. La page 4 000 est coûteuse. Pour renvoyer des pages profondes, le moteur doit trier et scanner un énorme ensemble de candidats, et ce pour chaque crawler qui parcourt la fin. Alors les opérateurs tracent une ligne. Au-delà de l’offset 10 000, la requête est simplement rejetée ou renvoie du vide.

Des moteurs de recherche comme Elasticsearch le rendent explicite. Son index.max_result_window a une valeur par défaut d’exactement 10 000, et la documentation vous invite à utiliser search_after ou l’API scroll plutôt que la pagination profonde from/size. Quand un site est construit sur cette pile, le plafond de 10 000 est hérité directement du moteur.

L’enseignement important : le plafond s’applique à une seule requête, pas à l’ensemble du catalogue. Le catalogue compte des millions d’éléments. Toute requête individuelle ne peut exposer que les 10 000 premiers. La solution n’est donc pas de paginer plus fort. La solution est de poser plus de questions, et plus petites.

D’abord, confirmez que c’est un plafond et non un blocage

Avant de repenser votre crawler, écartez un blocage discret. Les pages profondes vides et les blocages furtifs peuvent sembler identiques de l’extérieur, alors vérifiez trois choses :

  1. La page est-elle structurellement valide ? Une page réellement plafonnée a l’en-tête, le pied de page et les messages « aucun résultat » normaux du site. Un blocage renvoie souvent une page allégée, un défi ou un interstitiel. Si vous ne savez pas comment les distinguer, notre article sur la détection des blocages discrets couvre les signaux.
  2. La limite se reproduit-elle ? Demandez la même page profonde trois fois. Un plafond est déterministe : la page 70 est toujours vide. Un blocage est souvent intermittent et disparaît après une pause ou une nouvelle session.
  3. Une page moins profonde fonctionne-t-elle encore dans la même session ? Si la page 5 renvoie des données mais pas la page 70, avec les mêmes cookies et en-têtes, vous êtes face à un plafond, pas à un bannissement d’IP.

Une fois que vous avez confirmé qu’il s’agit d’un plafond, cessez d’essayer de le franchir. Aucune quantité de proxies ou de nouvelles tentatives ne déplace une limite d’offset stricte.

La solution : partitionner l’espace de requête

Toute la technique tient en une seule idée. Divisez le catalogue en morceaux assez petits pour que chacun contienne moins de 10 000 résultats, scrapez chaque morceau intégralement, puis fusionnez et dédupliquez. Toute grande interface de recherche vous offre des filtres, et chaque filtre est un couteau avec lequel découper.

Découper par catégorie

La coupe la plus naturelle. Au lieu de chercher « ordinateurs portables » (500 000 résultats, plafonnés à 10 000), cherchez chaque sous-catégorie : « portables gaming », « portables bureautiques », « chromebooks », etc. Les filtres de catégorie sont presque toujours exposés comme paramètres d’URL, ce qui rend leur itération triviale.

Découper par tranche de prix

Le prix est un axe numérique propre. La plupart des interfaces de recherche acceptent un prix minimum et maximum. Parcourez la plage par tranches :

def tranches_prix(bas, haut, pas):
    bord = bas
    while bord < haut:
        yield (bord, min(bord + pas, haut))
        bord += pas

# 0-25, 25-50, 50-75, ...
for mini, maxi in tranches_prix(0, 1000, 25):
    scrape_requete(prix_min=mini, prix_max=maxi)

Si une seule tranche renvoie encore plus de 10 000 résultats, découpez-la davantage. Des tranches étroites dans le bas de gamme, où l’inventaire se concentre, et des tranches plus larges dans le haut.

Découper par fenêtre temporelle

Pour tout ce qui est trié par nouveauté, comme les offres d’emploi, les actualités ou les articles récemment mis en ligne, découpez par temps. Demandez « publié dans les dernières 24 heures », reculez d’un jour, répétez. Le découpage par date a un bonus : il transforme un crawl ponctuel en un crawl incrémental et maintenable. Après le premier balayage complet, vous ne tirez plus que la fenêtre la plus récente.

Découper par localisation

Région, pays, ville ou code postal. Cet axe fonctionne bien pour les places de marché et les annuaires où la même requête à des endroits différents renvoie un inventaire en grande partie différent. Mais ne supposez pas que les morceaux sont disjoints. Le même vendeur ou la même annonce peut apparaître sous plusieurs régions, ce qui explique justement pourquoi l’étape de déduplication ci-dessous compte.

Combinez les axes quand un seul ne suffit pas

Si la catégorie seule fait encore sauter le plafond, combinez les axes : catégorie multipliée par tranche de prix, ou localisation multipliée par fenêtre temporelle. Chaque axe supplémentaire multiplie votre nombre de morceaux mais réduit chacun sous le plafond. L’objectif est simple : aucune requête individuelle ne doit renvoyer plus de ~10 000 résultats.

Dédupliquez entre les morceaux

Les morceaux qui se chevauchent feront réapparaître les mêmes éléments. Il vous faut une clé d’identité stable et un ensemble de « déjà vus ». Préférez l’identifiant permanent du site lui-même, généralement intégré dans l’URL de l’élément, à toute donnée cosmétique comme un titre ou un nom affiché qui peut changer d’une requête à l’autre.

vus = set()
resultats = []

for requete_morceau in construire_morceaux():
    for item in scrape_toutes_les_pages(requete_morceau):
        item_id = extraire_id(item["url"])   # stable, depuis l'URL
        if item_id in vus:
            continue
        vus.add(item_id)
        resultats.append(item)

Conservez l’ensemble des « déjà vus » de manière persistante entre les exécutions si vous scrapez selon un calendrier. Ainsi un balayage incrémental saute ce que vous possédez déjà, et votre budget de requêtes va vers des éléments réellement nouveaux.

Surveillez le rendement marginal

Le partitionnement est puissant mais pas gratuit. Chaque morceau coûte des requêtes, et les morceaux se chevauchent, donc vous payez pour des doublons. Suivez combien d’éléments nouveaux chaque morceau apporte. Quand un morceau ne renvoie presque rien que vous n’ayez déjà vu, vous êtes proche de la saturation et pouvez arrêter. Cette règle « on arrête quand c’est sec » vous évite de moudre des milliers de requêtes quasi vides à la poursuite de la dernière poignée d’éléments de longue traîne. Pour la plupart des projets, capturer l’essentiel du catalogue coûte une fraction de ce que coûterait la couverture complète de chaque entrée isolée.

Soyez aussi mesuré. Les pages de recherche profondes figurent parmi les plus lourdes qu’un site rende, et les marteler en parallèle est le moyen le plus rapide de transformer un crawl propre en une vague d’erreurs 503. Gardez une concurrence modérée sur ces points d’accès et laissez les morceaux, et non le parallélisme brut, assurer la montée en charge.

FAQ

Le plafond est-il toujours de 10 000 ? Non, mais c’est la valeur la plus courante car c’est celle par défaut d’Elasticsearch. Certains sites plafonnent à 1 000, d’autres à 25 000. Mesurez-le : recherchez par dichotomie la profondeur à laquelle les résultats se vident, et concevez les morceaux pour rester confortablement sous ce nombre.

Puis-je utiliser une API pour éviter le plafond ? Parfois. Les API basées sur un curseur (search_after, jetons de scroll, curseurs « page suivante ») ne souffrent pas du problème d’offset, donc si une API documentée propose une pagination par curseur, préférez-la. Mais la plupart des API publiques appliquent le même plafond de résultats par requête, donc vous devrez souvent quand même partitionner. Vérifiez toujours les conditions d’utilisation avant d’utiliser une API pour une collecte massive.

Pourquoi ne pas simplement augmenter la taille de page ? Parce que le plafond porte sur le total de résultats par requête, pas sur le nombre de pages. Une taille de page plus grande vous amène au plafond en moins de requêtes, mais le plafond ne bouge pas. Les grandes pages restent utiles pour réduire le volume de requêtes, mais n’attendez pas d’elles qu’elles débloquent plus de données.

Comment savoir si j’ai tout récupéré ? Vous ne le savez pas avec certitude. Utilisez le signal de rendement marginal : lorsque de nouveaux morceaux cessent d’ajouter des éléments, et que des axes indépendants (par exemple le découpage par catégorie et par prix) convergent vers le même total, vous avez de forts indices d’une couverture quasi complète.

En résumé

Les plafonds de résultats sont une réalité incontournable quand vous scrapez à grande échelle. L’instinct de paginer plus fort ou de lancer plus de proxies sur une page profonde est un effort gaspillé, car la limite est structurelle, pas défensive. Le mouvement fiable est de partitionner : découpez le catalogue par catégorie, prix, date et localisation jusqu’à ce que chaque requête tienne sous le plafond, puis fusionnez et dédupliquez sur une clé stable.

La seule chose que le partitionnement ne résout pas, c’est d’obtenir chaque page individuelle en premier lieu. Les pages de recherche profondes sont précisément là où les systèmes anti-bot se concentrent, et un crawler qui déclenche des centaines de requêtes de rendu lourdes attirera l’attention, aussi proprement que vous découpiez. Si vous préférez ne pas maintenir la rotation de proxies et le fingerprinting de navigateur qu’exige une récupération fiable des pages, ScrapeUnblocker gère cette couche et vous renvoie le HTML rendu, pour que vous puissiez vous concentrer sur la logique de découpage. Dans tous les cas, la stratégie est la même : cessez de lutter contre le plafond, et commencez à diviser le problème.

Essayez ScrapeUnblocker gratuitement

Taux de réussite de plus de 95 % · à partir de 0,55 € pour 1 000 appels · 500 requêtes gratuites à l'inscription.

Essayer gratuitement → Voir les tarifs