OpenSea est l'une des plus grandes places de marché NFT, et chaque page de collection et de fiche porte exactement le type de données structurées qu'un tracker de prix NFT, un carnet de recherche de marché ou un tableau de bord de rareté recherche : un nom d'article, la collection à laquelle il appartient, le prix actuel de la fiche en ETH, la dernière vente, l'identifiant de token et l'image. Le problème est qu'OpenSea est une application React rendue côté client et très lourde en JavaScript, donc une simple requête HTTP vous renvoie une coque presque vide à la place des articles que vous cherchez.
Ce guide vous montre comment scraper des données OpenSea avec Python de façon fiable. Vous créez un petit script fonctionnel qui récupère une page de collection rendue via la Crawling API avec un token JavaScript, analyse chaque article avec BeautifulSoup, et affiche des enregistrements structurés propres. L'ensemble du tutoriel se limite aux données NFT publiques visibles par n'importe qui sur une page de collection, et la section sur la légalité vers la fin n'est pas un simple formulaire, donc lisez-la avant de pointer ce script sur un volume réel.
Ce que vous allez construire
Un script Python qui prend une URL de collection OpenSea publique, récupère le HTML rendu via la Crawling API, et extrait un enregistrement structuré pour chaque NFT sur la page. Nous utiliserons une collection publique comme exemple et extrairons ces champs de chaque carte d'article :
- Nom de l'article le nom unique du NFT individuel, par exemple "Courtyard #1024".
- Collection la collection à laquelle appartient l'article.
- Prix (ETH) le prix actuel de la fiche tel qu'affiché sur la carte.
- Dernière vente le prix auquel l'article a été vendu précédemment, quand la carte l'affiche.
- Identifiant de token l'identifiant unique sur la chaîne, utile pour suivre un token sur différentes plateformes.
- URL de l'image la source de la miniature de l'article.
- URL de l'article le lien vers la page de détail du NFT individuel.
Pourquoi une simple requête échoue sur OpenSea
Si vous demandez une URL de collection OpenSea avec un client HTTP basique, vous obtenez une réponse avec le statut 200 et presque aucune donnée NFT dans le corps. Deux facteurs jouent contre vous. Premièrement, OpenSea est une application React rendue côté client qui construit sa grille d'articles dans le navigateur, donc le HTML initial est un cadre qui ne se remplit qu'après que les scripts de la page s'exécutent et que les données de la place de marché se chargent via le réseau. Deuxièmement, OpenSea détecte rapidement le trafic automatisé : les adresses IP de datacenter et les comportements de requête qui ne ressemblent pas à un vrai navigateur sont confrontés à des défis ou bloqués avant d'atteindre la grille rendue.
Un scraper OpenSea fonctionnel a donc besoin de deux choses en une seule requête : un navigateur qui rend réellement la page, et une adresse IP que la plateforme perçoit comme un vrai visiteur. Vous pouvez assembler cela vous-même avec un navigateur sans interface graphique plus un pool de proxys résidentiels rotatifs, mais les assembler et les maintenir en bon état représente l'essentiel du travail. La Crawling API regroupe les deux en un seul appel : vous lui envoyez l'URL avec un token JavaScript, elle rend la page derrière une IP fiable, et vous renvoie du HTML prêt à analyser. Si le rendu côté client est nouveau pour vous, notre guide sur le crawling des sites JavaScript explique pourquoi le rendu est important.
Crawlbase propose deux types de tokens. Le token normal récupère du HTML statique ; le token JavaScript (JS) rend d'abord la page dans un vrai navigateur. OpenSea est rendu côté client, donc vous avez besoin du token JS ici. Utiliser le token normal renvoie le même cadre vide qu'une récupération simple, et il n'y a rien à analyser. Vous pouvez commencer avec jusqu'à 5 000 requêtes gratuites, sans carte bancaire.
Prérequis
Vous avez besoin de quelques éléments en place avant d'écrire du code. Aucun ne prend longtemps.
Python de base. Vous devez être à l'aise avec l'écriture et l'exécution d'un script Python et l'installation de paquets avec pip. Si BeautifulSoup est nouveau pour vous, notre guide d'utilisation de BeautifulSoup en Python couvre les bases d'analyse que ce tutoriel suppose acquises.
Python 3.8 ou ultérieur. Confirmez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org ou via une distribution comme Anaconda.
Un compte Crawlbase et un token JS. Inscrivez-vous, ouvrez votre tableau de bord, et copiez votre token JavaScript (JS) depuis la page de documentation du compte. Traitez le token comme un mot de passe : il authentifie vos requêtes, donc gardez-le hors du contrôle de version.
Configurer le projet
Créez un environnement virtuel pour isoler les dépendances du projet, puis installez les deux bibliothèques nécessaires au scraper.
python --version python -m venv opensea_env source opensea_env/bin/activate pip install crawlbase beautifulsoup4
Sous Windows, activez l'environnement avec opensea_env\Scripts\activate à la place de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel pour la Crawling API, et beautifulsoup4 analyse le HTML retourné pour extraire les champs individuels par sélecteur CSS.
Étape 1 : Récupérer la page de collection rendue
Commencez par obtenir la page complète. Importez la classe CrawlingAPI, initialisez-la avec votre token JS, et demandez l'URL de la collection. Vérifier le statut avant d'analyser permet de rendre les échecs visibles plutôt que silencieux.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) def crawl(page_url): options = {"ajax_wait": "true", "page_wait": 5000} response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None if __name__ == "__main__": page_url = "https://opensea.io/collection/courtyard-nft" html = crawl(page_url) print(html[:500] if html else "No HTML returned")
Les deux options d'attente sont importantes pour une cible rendue côté client comme celle-ci. ajax_wait demande à l'API d'attendre que le contenu asynchrone ait fini de se charger, ce qu'OpenSea utilise pour peupler sa grille d'articles, et page_wait attend un nombre fixe de millisecondes après le chargement pour que les cartes à rendu tardif apparaissent avant la capture de la page. Cinq secondes est un bon point de départ ; augmentez-les si les cartes reviennent vides. Exécutez le script avec python scraper.py et vous devriez voir du vrai balisage d'articles, pas le cadre vide qu'une récupération simple renverrait. Cela confirme que le rendu fonctionne avant que vous n'écriviez un seul sélecteur.
OpenSea nécessite une page React rendue derrière une IP fiable, en un seul appel. La Crawling API prend un token JS, exécute la page dans un vrai navigateur, attend le contenu AJAX qui charge la grille d'articles, fait tourner les IP résidentielles côté serveur, et vous remet du HTML prêt à analyser, vous évitant d'exploiter vous-même une flotte sans interface graphique et un pool de proxys. Pointez-la sur une collection publique sur le niveau gratuit d'abord.
Étape 2 : Analyser les cartes d'articles avec BeautifulSoup
Avec le HTML rendu en main, chargez-le dans BeautifulSoup et extrayez chaque NFT par son sélecteur. OpenSea organise sa collection sous forme de grille de cartes d'articles répétées, donc vous sélectionnez toutes les cartes en une fois puis lisez les mêmes champs de chacune. Inspectez la page en direct dans les outils de développement de votre navigateur pour confirmer les attributs actuels ; les sélecteurs ci-dessous correspondent à la mise en page au moment de la rédaction.
from bs4 import BeautifulSoup BASE = "https://opensea.io" def text_of(card, selector): el = card.select_one(selector) return el.get_text(strip=True) if el else None def token_id_from(href): # OpenSea item URLs end with the token id, e.g. /assets/.../1024 return href.rstrip("/").split("/")[-1] if href else None def parse_collection(html): soup = BeautifulSoup(html, "html.parser") cards = soup.select('article.AssetSearchList--asset') items = [] for card in cards: link = card.select_one("a.Asset--anchor") href = link["href"] if link else None img = card.select_one("img") items.append({ "name": text_of(card, 'span[data-testid="ItemCardFooter-name"]'), "price_eth": text_of(card, 'div[data-testid="ItemCardPrice"] span[data-id="TextBody"]'), "last_sale": text_of(card, 'div[data-testid="ItemCardPrice-secondary"]'), "token_id": token_id_from(href), "image_url": img["src"] if img else None, "item_url": BASE + href if href else None, }) return items
L'utilitaire text_of fait deux choses utiles à la fois : il interroge un seul élément à l'intérieur de la carte et retourne None quand cet élément est absent, au lieu de planter sur un appel .get_text() contre rien. Cela maintient l'extraction résiliente quand un champ est absent sur une carte donnée, ce qui est courant puisque tous les NFT n'affichent pas un prix de dernière vente ou une fiche actuelle. Le nom de l'article provient du span data-testid="ItemCardFooter-name", le prix de la fiche du span data-id="TextBody" imbriqué dans ItemCardPrice, et le lien de l'ancre Asset--anchor. L'identifiant de token est le dernier segment de chemin de ce lien, et l'URL complète de l'article est l'hôte de base joint au href relatif.
Les noms de classes et attributs data-testid d'OpenSea changent sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, pas comme un contrat. Quand un champ revient comme None pour toutes les cartes, réinspectez un article en direct dans les outils de développement de votre navigateur et mettez à jour le sélecteur. La maintenance périodique des sélecteurs est normale pour tout scraper en production, pas le signe que quelque chose est cassé.
Étape 3 : Gérer la pagination par défilement
Une page de collection ne charge pas tous les articles d'un coup. OpenSea utilise le défilement infini, donc plus de NFT n'apparaissent qu'à mesure que vous descendez dans la grille. Plutôt que de faire de l'ingénierie inverse sur les appels de pagination, vous laissez la Crawling API faire défiler la page pour vous avec les options scroll et scroll_interval. L'API fait défiler pendant le nombre de secondes que vous lui donnez, ce qui charge davantage de cartes dans le même HTML que vous analysez ensuite.
def crawl_with_scroll(page_url): options = { "ajax_wait": "true", "scroll": "true", "scroll_interval": "20", # scroll for 20 seconds, max 60 } response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None
Définir scroll à true demande à l'API de faire défiler la page rendue, et scroll_interval contrôle combien de temps, jusqu'à 60 secondes. Un intervalle plus long charge davantage d'articles mais prend plus de temps par requête, donc choisissez une valeur qui correspond à la profondeur à laquelle vous devez aller dans la collection. Notez que quand vous faites défiler, vous supprimez page_wait, car le défilement maintient déjà la page ouverte suffisamment longtemps pour que les nouvelles cartes se rendent.
Étape 4 : Assembler le tout
Reliez maintenant la récupération avec défilement et le parseur en un seul script exécutable. Récupérez le HTML rendu, transmettez-le au parseur, et écrivez le résultat en JSON pour pouvoir le charger n'importe où plus tard.
import json from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) BASE = "https://opensea.io" def crawl(page_url): options = {"ajax_wait": "true", "scroll": "true", "scroll_interval": "20"} response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None def text_of(card, selector): el = card.select_one(selector) return el.get_text(strip=True) if el else None def token_id_from(href): return href.rstrip("/").split("/")[-1] if href else None def parse_collection(html): soup = BeautifulSoup(html, "html.parser") cards = soup.select('article.AssetSearchList--asset') items = [] for card in cards: link = card.select_one("a.Asset--anchor") href = link["href"] if link else None img = card.select_one("img") items.append({ "name": text_of(card, 'span[data-testid="ItemCardFooter-name"]'), "price_eth": text_of(card, 'div[data-testid="ItemCardPrice"] span[data-id="TextBody"]'), "last_sale": text_of(card, 'div[data-testid="ItemCardPrice-secondary"]'), "token_id": token_id_from(href), "image_url": img["src"] if img else None, "item_url": BASE + href if href else None, }) return items def main(): page_url = "https://opensea.io/collection/courtyard-nft" html = crawl(page_url) if not html: return items = parse_collection(html) with open("opensea_data.json", "w") as f: json.dump(items, f, indent=2) print(f"Saved {len(items)} items") if __name__ == "__main__": main()
À quoi ressemble le résultat
Exécutez le script complet avec python scraper.py et vous obtenez un enregistrement structuré propre pour chaque NFT, prêt à écrire en JSON, CSV ou dans une base de données. Les champs prix et dernière vente reviennent sous forme de chaînes qu'OpenSea affiche, symbole ETH compris, donc normalisez-les en aval si vous avez besoin de valeurs numériques.
[ { "name": "Courtyard #1024", "price_eth": "0.018 ETH", "last_sale": "Last sale: 0.015 ETH", "token_id": "1024", "image_url": "https://i.seadn.io/s/raw/files/abc123.png", "item_url": "https://opensea.io/assets/matic/0x251be3.../1024" }, { "name": "Courtyard #2087", "price_eth": "0.021 ETH", "last_sale": null, "token_id": "2087", "image_url": "https://i.seadn.io/s/raw/files/def456.png", "item_url": "https://opensea.io/assets/matic/0x251be3.../2087" } ]
Le deuxième enregistrement a une dernière vente null, ce qui est l'utilitaire qui fait son travail : tous les articles n'ont pas encore été vendus, donc ce champ est simplement absent plutôt que générateur d'une erreur.
Scraper les pages de détail des NFT
La page de collection vous donne un résumé au niveau de la carte, mais chaque NFT possède aussi sa propre page de détail avec des champs plus riches, notamment une description plus longue, l'historique complet des prix et le rang de rareté lorsque la collection en publie un. L'approche est la même que pour la page de collection : rendez l'URL avec le token JS, puis lisez les champs par sélecteur. Les sélecteurs diffèrent car la mise en page de détail est différente, donc inspectez une page d'article en direct avant de vous y fier.
def parse_nft_detail(html, url): soup = BeautifulSoup(html, "html.parser") rank = soup.select_one('[data-testid="rarity-rank"]') return { "name": text_of(soup, "h1.item--title"), "collection": text_of(soup, "a.item--collection-detail"), "price_eth": text_of(soup, "div.Price--amount"), "rarity_rank": rank.get_text(strip=True) if rank else None, "token_id": token_id_from(url), "item_url": url, }
Cela réutilise les mêmes utilitaires text_of et token_id_from du scraper de collection, donc une exécution de détail n'est qu'un ensemble de sélecteurs différent sur la même boucle récupération-puis-analyse. Le nom de l'article provient de l'en-tête item--title, le prix de la fiche de Price--amount, et le rang de rareté d'un identifiant de test rarity-rank quand la collection en expose un. Quand une collection n'a pas de classement de rareté, ce champ reste None, ce qui est correct plutôt qu'un bug.
Mise à l'échelle et maintien du non-blocage
Une seule collection est une démonstration ; un vrai travail s'exécute sur plusieurs collections. La forme reste la même : gardez une liste d'URL de collections, récupérez chacune via la Crawling API avec le défilement activé, analysez-la avec la même fonction, et collectez les lignes. Comme chaque page de collection partage la même structure de carte, le parseur que vous avez déjà écrit fonctionne sur toutes sans modification. Même avec le rendu pris en charge, OpenSea surveille le trafic à l'allure d'un scraper, donc quelques bonnes habitudes maintiennent une exécution en bonne santé, et elles s'appliquent à toute cible difficile.
- Cadencez vos requêtes. Marteler les pages de collection en boucle serrée est le moyen le plus rapide d'être limité en débit. Étalez les requêtes et variez vos cibles au lieu de crawler une seule collection à pleine vitesse.
- Appuyez-vous sur la rotation. Un pool d'IP résidentielles répartit les requêtes sur de nombreuses adresses d'utilisateurs réels pour qu'aucune ne déclenche une limite de débit. La Crawling API gère cela pour vous ; si vous construisez votre propre infrastructure, c'est la partie à bien soigner.
- Lisez les codes de statut. Une exécution qui commence à renvoyer des défis ou des erreurs vous indique que le débit ou le niveau d'IP actuel n'est plus suffisant. Traitez cela comme un signal pour ralentir, pas comme du bruit à ignorer.
Pour le guide complet, consultez comment scraper des sites web sans être bloqué. Et si vous préférez acheminer votre propre trafic via un pool rotatif plutôt que d'utiliser l'API gérée, le Smart AI Proxy (également appelé AI Proxy) vous donne la même rotation d'IP résidentielles sous forme de point de terminaison proxy.
Est-il légal de scraper OpenSea ?
Le fait que le scraping d'OpenSea soit autorisé dépend des conditions d'utilisation d'OpenSea, de votre juridiction et de ce que vous faites avec les données. Les conditions d'OpenSea imposent des limites à l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quel que soit le soin apporté à vos outils. Aucun code ici ne change cela ; il rend simplement la partie technique fonctionnelle. Lisez les Conditions d'utilisation d'OpenSea et son robots.txt, et traitez les deux comme la limite de ce que vous collectez.
Quelques lignes à respecter. Collectez uniquement les données NFT publiques : le nom de l'article, la collection, le prix de la fiche, la dernière vente, l'identifiant de token, l'image et le lien que n'importe qui peut voir sur une page de collection sans compte. Respectez les attentes déclarées d'OpenSea en matière de débit et maintenez votre volume de requêtes suffisamment bas pour ne pas solliciter excessivement ses serveurs. Les métadonnées du token lui-même se trouvent sur la chaîne, donc pour de nombreux cas d'usage, lire la blockchain directement est à la fois plus propre et sans ambiguïté. Ne redistribuez pas l'art ou les médias liés à un NFT comme s'ils étaient les vôtres ; l'URL de l'image est correcte à référencer, les médias sous-jacents appartiennent au créateur.
Pour un usage en production ou commercial, OpenSea publie une API officielle, et c'est la voie qu'il entend que les développeurs empruntent pour les données de la place de marché. Elle vous donne des fiches structurées, des événements et des statistiques de collection dans des conditions claires, sans rendre des pages ni maintenir des sélecteurs. Le scraping convient à la recherche exploratoire et à l'analyse ponctuelle sur des données publiques ; si votre projet nécessite un accès continu, à fort volume ou commercial, l'API officielle ou un accord de données direct est la bonne voie, pas un scraper plus sophistiqué.
Points clés
- OpenSea est une application React rendue côté client. Une récupération simple renvoie un cadre vide, donc vous devez rendre la page avant de l'analyser.
-
Utilisez le token JS. La Crawling API avec un token JavaScript rend la page derrière une IP fiable en un seul appel ;
ajax_waitetpage_waitcontrôlent combien de temps elle attend le contenu. -
Le défilement gère la pagination. OpenSea charge les articles en défilement infini, donc passez
scrolletscroll_intervalau lieu de faire de l'ingénierie inverse sur sa pagination. - BeautifulSoup effectue l'extraction. Sélectionnez toutes les cartes d'articles, puis lisez le nom, le prix en ETH, la dernière vente, l'identifiant de token, l'image et le lien de chacune, et attendez-vous à ce que les sélecteurs dérivent.
- Restez sur les données publiques et préférez l'API officielle pour la production. Respectez les CGU et le robots.txt d'OpenSea, limitez-vous aux données NFT publiques, et utilisez l'API officielle pour les travaux commerciaux ou à fort volume.
Foire aux questions
Pourquoi une récupération simple ne renvoie-t-elle aucun NFT d'OpenSea ?
Parce qu'OpenSea est une application React rendue côté client qui construit sa grille d'articles dans le navigateur. Le HTML initial est un cadre qui ne se remplit qu'après que les scripts de la page s'exécutent et que les données de la place de marché se chargent via le réseau, donc une requête HTTP brute renvoie un statut 200 avec la grille vide. Pour obtenir de vraies données, vous devez d'abord rendre la page, ce que le token JS de la Crawling API gère pour vous.
Ai-je besoin du token normal ou du token JS pour OpenSea ?
Le token JS. Le token normal récupère le HTML statique, qui sur OpenSea est le même cadre vide qu'une récupération simple renvoie. Le token JS rend la page dans un vrai navigateur avant de renvoyer le HTML, de sorte que les cartes d'articles sont présentes lorsque BeautifulSoup les analyse.
Comment charger plus que le premier écran d'articles ?
OpenSea charge les articles en défilement infini, donc le reste n'apparaît qu'à mesure que vous descendez dans la grille. Passez scroll défini à true et un scroll_interval en secondes à la Crawling API, et elle fait défiler la page rendue pour vous avant de capturer le HTML, de sorte que davantage de cartes sont présentes lorsque vous analysez. Un intervalle plus long charge davantage d'articles au coût d'une requête plus lente.
Mes sélecteurs retournent None pour chaque carte. Qu'est-ce qui a changé ?
Presque certainement le balisage d'OpenSea. Ses noms de classes et attributs data-testid changent sans préavis, donc des sélecteurs qui fonctionnaient le mois dernier peuvent se casser. Réinspectez un article en direct dans les outils de développement de votre navigateur et mettez à jour les sélecteurs. La maintenance périodique des sélecteurs est normale pour tout scraper en production.
Dois-je scraper OpenSea ou utiliser son API officielle ?
Pour la recherche exploratoire et l'analyse ponctuelle sur des données publiques, scraper une page de collection est correct et c'est ce que ce guide couvre. Pour un usage en production, commercial ou à fort volume, préférez l'API officielle d'OpenSea : elle renvoie des fiches structurées, des événements et des statistiques de collection dans des conditions claires, sans rendre des pages ni poursuivre les changements de sélecteurs.
Puis-je scraper des données de compte ou personnelles d'OpenSea ?
Non, et ce guide ne le couvre pas. Les détails de compte de portefeuille, tout ce qui se trouve derrière une connexion, et l'art ou les médias que vous redistribueriez se trouvent tous en dehors des données NFT publiques, donc ils ne sont pas dans la portée ici et vont à l'encontre des conditions d'OpenSea. Restez sur les données publiques d'articles, de collections et de fiches que n'importe qui peut voir, et lisez la blockchain directement quand vous avez besoin de métadonnées de token faisant autorité.
Crawlez n'importe quel site à grande échelle, sans combattre l'infrastructure.
Crawlbase gère les proxies, les empreintes et les CAPTCHA afin que votre équipe livre des pipelines de données au lieu de maintenir la plomberie de crawl. 1 000 requêtes gratuites, sans carte requise.
