Aller au contenu
Retour au blog

Comment analyser du JSON en Python : des réponses d'API au JSON caché dans les pages Web

Suciu DanDernière mise à jour le 14 min read
Comment analyser du JSON en Python : des réponses d'API au JSON caché dans les pages Web
En bref : Pour analyser du JSON en Python, utilisez le module json intégré : json.loads() pour une chaîne ou des octets, json.load() pour un fichier, et response.json() de Requests pour une réponse HTTP. Vérifiez séparément le statut HTTP et la validité du JSON, car une réponse 200 peut tout de même être du HTML. Ce guide couvre aussi les erreurs de décodage, l'accès aux données imbriquées, la validation, les gros fichiers et le JSON caché dans les pages web.

Le module intégré de Python json analyse le format JSON. Utilisez json.loads() pour une chaîne de caractères ou des octets, json.load() pour un fichier, et la méthode response.json() pour une réponse HTTP. Ce guide aborde les erreurs de décodage, l’accès imbriqué en toute sécurité, la validation, les fichiers volumineux, la précision des nombres et le JSON dissimulé dans les pages web.

Points clés à retenir

  • Privilégiez json.loads pour les chaînes de caractères/octets et json.load pour les fichiers ; ouvrez les fichiers en UTF-8 (ou utf-8-sig lorsqu’un BOM est présent).
  • Traitez le statut HTTP et la validité JSON comme des vérifications distinctes : une réponse 200 peut tout de même être du code HTML.
  • Parcourez les données imbriquées complexes à l’aide de .get() des chaînes ou un petit dig() assistant ; validez avec Pydantic ou JSON Schema après analyse.
  • Traitez les tableaux volumineux avec ijson, ajoutez les résultats de scraping sous forme de lignes JSON, et mesurez orjson sur vos propres charges utiles.
  • Privilégiez le JSON intégré à la page (JSON-LD, __NEXT_DATA__) ou les champs structurés d’une API de scraping plutôt que des sélecteurs CSS fragiles lorsque la mise en page HTML change fréquemment.

Comment analyser une chaîne JSON en Python ?

Appelez json.loads() sur un str ou bytes valeur. Elle renvoie un objet Python — généralement un dict ou list— qui reflète la structure JSON.

import json

raw = """
{
  "sku": "WSA-BOTTLE-750",
  "title": "Insulated Steel Water Bottle 750ml",
  "price": 19.99,
  "currency": "USD",
  "in_stock": true,
  "variants": [
    {"color": "matte black", "capacity_ml": 750},
    {"color": "ocean blue", "capacity_ml": 750}
  ],
  "seller": {
    "name": "TrailGear Co",
    "rating": 4.8,
    "country": "US"
  }
}
"""

product = json.loads(raw)
print(product["sku"], product["variants"][0]["color"])
# WSA-BOTTLE-750 matte black

# bytes work the same way
product_from_bytes = json.loads(raw.encode("utf-8"))
assert product_from_bytes["title"] == product["title"]

La même fiche produit apparaît tout au long de l'exemple ; chaque exemple s'appuie donc sur un enregistrement de type « scraped ».

Comment lire un fichier JSON en Python ?

Ouvrez le fichier avec l’encodage UTF-8 et transmettez l’objet fichier à json.load().

import json
from pathlib import Path

path = Path("product.json")
with path.open(encoding="utf-8") as f:
    product = json.load(f)

print(product["currency"])  # USD

Que faire lorsqu’un fichier JSON commence par un BOM ?

Certains éditeurs enregistrent les fichiers UTF-8 avec une marque d’ordre des octets (EF BB BF). Ce caractère invisible se trouve avant le premier { et empêche un décodage normal. Ouvrez le fichier avec encoding="utf-8-sig" afin que Python supprime le BOM avant json.load s'exécute. Si vous disposez déjà d'octets, décodez-les avec utf-8-sig puis appelez json.loads.

import json
from pathlib import Path

bom_path = Path("product_bom.json")
with bom_path.open(encoding="utf-8-sig") as f:
    product = json.load(f)

# Or from bytes:
product = json.loads(bom_path.read_bytes().decode("utf-8-sig"))

Correspondance entre les types JSON et les types Python

Les types JSON correspondent aux types Python tels que définis par la grammaire JSON sur json.org et par la bibliothèque standard :

JSON

Python (json.loads)

Python → JSON (json.dumps)

objet

dict

dict

tableau

list

list / tuple

chaîne

str

str

nombre (entier)

int

int

nombre (réel)

float

float / int

true / false

True / False

bool

null

None

None

Pourquoi vos clés et vos tuples changent après un aller-retour

Les noms des objets JSON sont toujours des chaînes de caractères. Les clés de dictionnaire de type entier deviennent des chaînes de caractères après dumps → loads. Les tuples sont convertis en listes car JSON ne prend en charge que les tableaux. Les ensembles ne sont pas sérialisables et génèrent une TypeError.

import json

print(json.loads(json.dumps({1: "a", 2: "b"})))
# {'1': 'a', '2': 'b'}

print(json.loads(json.dumps({"coords": (10, 20)})))
# {'coords': [10, 20]}

try:
    json.dumps({"tags": {"refillable", "bpa-free"}})
except TypeError as e:
    print(e)
# Object of type set is not JSON serializable

Comment analyser une réponse JSON provenant d’une API ?

Appelez response.raise_for_status() pour les erreurs HTTP, puis response.json() pour décoder le corps de la réponse. Les codes d’état et la validité du JSON sont deux choses distinctes : un code d’état positif ne garantit pas que le corps de la réponse soit au format JSON.

import requests

resp = requests.get("https://httpbin.org/json", timeout=15)
resp.raise_for_status()
data = resp.json()
print(type(data), list(data)[:3])

Lorsqu’une réponse 200 n’est pas au format JSON

Les pages de blocage et de vérification renvoient souvent du code HTML avec un code HTTP 200. Vérifiez Content-Type, détectez les échecs de décodage et affichez un aperçu court et sans risque du corps :

import json
from typing import Any

import requests


def get_json(url: str, timeout: float = 15.0) -> Any:
    resp = requests.get(url, timeout=timeout)
    resp.raise_for_status()
    content_type = resp.headers.get("Content-Type", "")
    if "json" not in content_type.lower() and "javascript" not in content_type.lower():
        preview = resp.text[:120].replace("\n", " ")
        raise ValueError(
            f"Expected JSON Content-Type, got {content_type!r}. "
            f"Body starts: {preview!r}"
        )
    try:
        return resp.json()
    except json.JSONDecodeError as e:
        preview = resp.text[:120].replace("\n", " ")
        raise ValueError(
            f"JSON decode failed at pos {e.pos}: {e.msg}. "
            f"Body starts: {preview!r}"
        ) from e


product_feed = get_json("https://httpbin.org/json")
# get_json("https://httpbin.org/html")  # raises ValueError

Une page HTML avec un statut 200 est souvent une page de blocage ; notre guide sur le scraping sans se faire bloquer explique comment l’éviter.

Comment corriger une erreur JSONDecodeError ?

JSONDecodeError signifie que les données d’entrée ne sont pas au format JSON valide : corps vide, charge utile tronquée, code HTML, guillemets simples, virgule finale ou autre problème similaire. Lire e.msg, e.lineno, e.colno, puis e.pos, puis affichez le texte autour de pos afin de repérer l’erreur. Vous trouverez tous les détails concernant les attributs et les hooks dans la documentation du module Python `json`.

import json


def locate_decode_error(text: str) -> None:
    try:
        json.loads(text)
    except json.JSONDecodeError as e:
        start = max(0, e.pos - 20)
        end = min(len(text), e.pos + 20)
        print(f"{e.msg} at line {e.lineno}, col {e.colno}, pos {e.pos}")
        print(f"Around error: {text[start:end]!r}")


locate_decode_error('{"sku": "WSA-BOTTLE-750", "price":')
# Expecting value at line 1, col 36, pos 35

Causes courantes et à quoi chacune d'entre elles ressemble

Symptôme (extrait de message)

Cause probable

Solution

Expecting value à la position 0

Corps vide ou HTML/undefined

Vérifiez le transport ; assurez-vous d’avoir récupéré le JSON

Expecting value au milieu de la chaîne

Réponse tronquée

Réessayer, augmenter les délais d'attente ou utiliser un flux avec vérification de la longueur

Expecting property name enclosed in double quotes

Clés entre guillemets simples ou virgule finale

Corrigez le producteur ; ne « réparez » pas avec eval

Extra data

Deux valeurs JSON dans une seule chaîne

Divisez les lignes JSON ou analysez une valeur à la fois

Balises HTML dans l’aperçu

Blocage logiciel ou page de connexion

Corrigez les en-têtes, les cookies ou le rendu ; consultez le guide de dépannage ci-dessus

Comment accéder en toute sécurité à du JSON imbriqué ?

Indexation « hard » (data["seller"]["rating"]) génère une KeyError lorsqu’une clé est manquante. .get() Les chaînes renvoient une valeur par défaut, mais elles génèrent beaucoup de bruit sur les chemins profonds. Une petite fonction d'aide typée dig() parcourt les clés des dictionnaires et les index des listes, et traite un chemin manquant différemment d’un null.

from typing import Any


def dig(data: Any, *path: str | int, default: Any = None) -> Any:
    cur: Any = data
    for key in path:
        if isinstance(cur, dict):
            if key not in cur:
                return default
            cur = cur[key]
        elif isinstance(cur, list) and isinstance(key, int):
            if key < 0 or key >= len(cur):
                return default
            cur = cur[key]
        else:
            return default
    return cur


product = {
    "variants": [{"color": "matte black", "capacity_ml": 750}],
    "seller": {"name": "TrailGear Co", "rating": 4.8},
}

print(dig(product, "seller", "rating"))                 # 4.8
print(dig(product, "variants", 0, "color"))             # matte black
print(dig(product, "variants", 9, "color", default="?"))  # ?
print(dig({"seller": {"rating": None}}, "seller", "rating", default="missing"))
# None  — key exists, value is null
print(dig({"seller": {}}, "seller", "rating", default="missing"))
# missing — key absent

Comment valider des données JSON en Python ?

L’analyse syntaxique ne vérifie que la syntaxe. Après loads, vérifiez les types et les champs obligatoires avec Pydantic ou JSON Schema.

from decimal import Decimal

from pydantic import BaseModel, ValidationError


class Variant(BaseModel):
    color: str
    capacity_ml: int


class Seller(BaseModel):
    name: str
    rating: float
    country: str


class Product(BaseModel):
    sku: str
    title: str
    price: Decimal
    currency: str
    in_stock: bool
    variants: list[Variant]
    seller: Seller
    notes: str | None = None


product = Product.model_validate(
    {
        "sku": "WSA-BOTTLE-750",
        "title": "Insulated Steel Water Bottle 750ml",
        "price": 19.99,
        "currency": "USD",
        "in_stock": True,
        "variants": [{"color": "matte black", "capacity_ml": 750}],
        "seller": {"name": "TrailGear Co", "rating": 4.8, "country": "US"},
    }
)
print(product.price)  # 19.99

try:
    Product.model_validate(
        {
            "sku": "WSA-BOTTLE-750",
            "title": "Insulated Steel Water Bottle 750ml",
            "price": "cheap",
            "currency": "USD",
            "in_stock": True,
            "variants": [],
            "seller": {"name": "TrailGear Co", "rating": 4.8, "country": "US"},
        }
    )
except ValidationError as e:
    print(e.error_count(), "validation error(s)")

La même structure qu’un JSON Schema, validée avec le jsonschema (voir aussi Comprendre JSON Schema) :

import jsonschema

schema = {
    "type": "object",
    "required": ["sku", "title", "price", "currency", "in_stock", "variants", "seller"],
    "properties": {
        "sku": {"type": "string"},
        "title": {"type": "string"},
        "price": {"type": "number"},
        "currency": {"type": "string", "minLength": 3, "maxLength": 3},
        "in_stock": {"type": "boolean"},
        "variants": {
            "type": "array",
            "items": {
                "type": "object",
                "required": ["color", "capacity_ml"],
                "properties": {
                    "color": {"type": "string"},
                    "capacity_ml": {"type": "integer"},
                },
            },
        },
        "seller": {
            "type": "object",
            "required": ["name", "rating", "country"],
            "properties": {
                "name": {"type": "string"},
                "rating": {"type": "number"},
                "country": {"type": "string"},
            },
        },
    },
}

jsonschema.validate(
    {
        "sku": "WSA-BOTTLE-750",
        "title": "Insulated Steel Water Bottle 750ml",
        "price": 19.99,
        "currency": "USD",
        "in_stock": True,
        "variants": [{"color": "matte black", "capacity_ml": 750}],
        "seller": {"name": "TrailGear Co", "rating": 4.8, "country": "US"},
    },
    schema,
)

Comment écrire du JSON dans une chaîne de caractères ou un fichier ?

Utilisez json.dumps pour une chaîne de caractères et json.dump pour un fichier. indent met en forme, sort_keys=True stabilise les différences et separators=(",", ":") réduit la taille des données utiles.

import json
from pathlib import Path

product = {
    "sku": "WSA-BOTTLE-750",
    "title": "Insulated Steel Water Bottle 750ml",
    "price": 19.99,
    "currency": "USD",
    "in_stock": True,
}

print(json.dumps(product, indent=2, sort_keys=True))
print(json.dumps(product, separators=(",", ":")))

with Path("product_out.json").open("w", encoding="utf-8") as f:
    json.dump(product, f, ensure_ascii=False, indent=2)

Écriture de texte non anglais sans séquences d'échappement

Par défaut, dumps échappe les caractères non ASCII sous la forme \uXXXX. Passez l’option ensure_ascii=False lorsque vous souhaitez un format UTF-8 lisible dans les fichiers et les journaux.

import json

title = {"title": "Bouteille d'eau 750ml — acier"}
print(json.dumps(title))
# {"title": "Bouteille d'eau 750ml \u2014 acier"}
print(json.dumps(title, ensure_ascii=False))
# {"title": "Bouteille d'eau 750ml — acier"}

Sérialisation des classes `datetime`, `Decimal` et `dataclasses`

Passez une default= fonction pour les types que l’encodeur ne reconnaît pas. Vous pouvez également créer une sous-classe de json.JSONEncoder lorsque vous avez besoin d’une classe d’encodeur réutilisable.

import json
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
from decimal import Decimal
from typing import Any


def default_encoder(obj: Any) -> Any:
    if isinstance(obj, datetime):
        return obj.isoformat()
    if isinstance(obj, Decimal):
        return str(obj)
    if hasattr(obj, "__dataclass_fields__"):
        return asdict(obj)
    raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")


@dataclass
class Offer:
    sku: str
    price: Decimal
    listed_at: datetime


offer = Offer(
    sku="WSA-BOTTLE-750",
    price=Decimal("19.99"),
    listed_at=datetime(2026, 10, 6, 12, 0, tzinfo=timezone.utc),
)
print(json.dumps(offer, default=default_encoder))
# {"sku": "WSA-BOTTLE-750", "price": "19.99", "listed_at": "2026-10-06T12:00:00+00:00"}

Comment garantir la précision des prix et des grands nombres ?

Utilisez parse_float=Decimal de sorte que les valeurs telles que 19.99 restent exactes sous forme de nombres décimaux plutôt que de nombres à virgule flottante binaires. La RFC 8259, la norme JSON, n’autorise pas NaN ni Infinity. Après l'analyse syntaxique, le module `decimal` de Python est généralement utilisé pour les valeurs monétaires.

import json
from decimal import Decimal

data = json.loads('{"price": 19.99}', parse_float=Decimal)
print(data["price"], type(data["price"]))
# 19.99 <class 'decimal.Decimal'>

# Reject NaN / Infinity when encoding
try:
    json.dumps({"price": float("nan")}, allow_nan=False)
except ValueError as e:
    print(e)

Le décodeur de Python accepte NaN et Infinity par défaut en tant qu’extension. Passez un parse_constant qui lève une exception si vous souhaitez les rejeter à l'entrée.

Comment détecter les clés en double dans un fichier JSON ?

La norme JSON RFC 8259 stipule que les noms d’objets doivent être uniques, mais json.loads conserve la dernière valeur sans avertissement. Une object_pairs_hook peut déclencher une exception à la place.

import json
from typing import Any


def detect_duplicates(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
    seen: set[str] = set()
    out: dict[str, Any] = {}
    for key, value in pairs:
        if key in seen:
            raise ValueError(f"Duplicate key: {key!r}")
        seen.add(key)
        out[key] = value
    return out


text = '{"sku": "A", "sku": "B", "price": 1}'
print(json.loads(text)["sku"])  # B — last value wins

try:
    json.loads(text, object_pairs_hook=detect_duplicates)
except ValueError as e:
    print(e)  # Duplicate key: 'sku'

Comment travailler avec des fichiers JSON Lines ?

Le format JSON Lines stocke une valeur JSON par ligne. Ajoutez un enregistrement après chaque extraction, puis lisez ligne par ligne, ignorez les espaces vides et signalez le numéro de ligne lorsqu’un enregistrement ne parvient pas à être décodé.

import json
from pathlib import Path

path = Path("products.jsonl")

# Append during a run
with path.open("a", encoding="utf-8") as f:
    for rec in (
        {"sku": "WSA-BOTTLE-750", "price": 19.99},
        {"sku": "WSA-LID-01", "price": 4.5},
    ):
        f.write(json.dumps(rec, ensure_ascii=False) + "\n")

# Read with line numbers
records = []
with path.open(encoding="utf-8") as f:
    for lineno, line in enumerate(f, start=1):
        line = line.strip()
        if not line:
            continue
        try:
            records.append(json.loads(line))
        except json.JSONDecodeError as e:
            raise ValueError(f"Invalid JSON on line {lineno}: {e.msg}") from e

print(len(records), "records")

Comment analyser un fichier JSON trop volumineux pour tenir en mémoire ?

Utilisez ijson sur PyPI : parcourez les éléments d’un chemin d’accès au lieu de charger l’intégralité du document. N’utilisez use_float=True uniquement lorsque la précision des nombres à virgule flottante binaires est acceptable.

import json
from decimal import Decimal
from pathlib import Path

import ijson

# Build a sample file so the example runs anywhere
large = Path("large_products.json")
items = [{"sku": f"SKU-{i}", "price": i * 1.5} for i in range(5_000)]
large.write_text(json.dumps({"items": items}), encoding="utf-8")

count = 0
total = Decimal("0")
with large.open("rb") as f:
    for item in ijson.items(f, "items.item"):
        count += 1
        total += Decimal(str(item["price"]))

print(count, "items; sum", total)

orjson est-il plus rapide que le module json ?

Souvent oui, mais effectuez des mesures sur vos propres données. La documentation d’orjson sur PyPI souligne les différences d’API qui comptent dans la pratique : dumps les bytes, les options sont des indicateurs plutôt que des arguments-clés, et l’indentation utilise OPT_INDENT_2 plutôt qu’un indent entier.

import json
import timeit

import orjson

payload = {
    "sku": "WSA-BOTTLE-750",
    "title": "Insulated Steel Water Bottle 750ml",
    "price": 19.99,
    "variants": [{"color": "matte black", "capacity_ml": 750}],
}

encoded = orjson.dumps(payload)
assert isinstance(encoded, bytes)
decoded = orjson.loads(encoded)
pretty = orjson.dumps(payload, option=orjson.OPT_INDENT_2)

def harness() -> None:
    orjson.dumps(payload)
    json.dumps(payload)

print(timeit.timeit(harness, number=5_000))
# Run locally and compare; numbers vary by machine and payload.

Comment extraire du JSON caché dans une page web ?

De nombreuses pages transmettent leurs données au format JSON dans des <script> balises, ce qui s’avère souvent plus fiable que les sélecteurs CSS sur le code HTML affiché. Analysez ce JSON, puis parcourez-le à l’aide des mêmes outils que ceux que vous utilisez pour les réponses d’API.

Lecture de données structurées JSON-LD

Le JSON-LD se trouve dans des script[type="application/ld+json"] blocs. Une page peut générer un objet unique, une liste ou un @graph conteneur. Filtrez par @type == "Product" (ou tout autre type dont vous avez besoin) :

import json
from typing import Any

from bs4 import BeautifulSoup

html = """
<!DOCTYPE html>
<html><head>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Insulated Steel Water Bottle 750ml",
  "sku": "WSA-BOTTLE-750",
  "offers": {"@type": "Offer", "price": "19.99", "priceCurrency": "USD"}
}
</script>
<script type="application/ld+json">
[
  {"@type": "BreadcrumbList", "itemListElement": []},
  {"@type": "Product", "name": "Spare Lid", "sku": "WSA-LID-01"}
]
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {"@type": "Organization", "name": "TrailGear Co"},
    {"@type": "Product", "name": "Bottle Bundle", "sku": "WSA-BUNDLE"}
  ]
}
</script>
</head><body></body></html>
"""


def extract_json_ld_products(page_html: str) -> list[dict[str, Any]]:
    soup = BeautifulSoup(page_html, "html.parser")
    products: list[dict[str, Any]] = []

    def collect(node: Any) -> None:
        if isinstance(node, list):
            for item in node:
                collect(item)
            return
        if not isinstance(node, dict):
            return
        if node.get("@type") == "Product":
            products.append(node)
        graph = node.get("@graph")
        if isinstance(graph, list):
            collect(graph)

    for tag in soup.select('script[type="application/ld+json"]'):
        if not tag.string:
            continue
        collect(json.loads(tag.string))
    return products


for product in extract_json_ld_products(html):
    print(product["sku"], product["name"])

Lorsqu’une page intègre son JSON-LD via JavaScript, un navigateur hébergé qui affiche la page avant de la renvoyer vous fournit le code HTML final.

Lecture __NEXT_DATA__ et d’autres états du framework

__NEXT_DATA__ et d’autres états du framework

Next.js et les frameworks similaires intègrent les props de la page dans une balise script#__NEXT_DATA__ balise. Analysez le contenu de cette balise, puis parcourez-le à l’aide de dig(). Pour un tutoriel complet sur le secteur de la vente au détail, consultez notre guide de scraping de Walmart, qui analyse la fiche produit à partir de __NEXT_DATA__.

import json
from typing import Any

from bs4 import BeautifulSoup


def dig(data: Any, *path: str | int, default: Any = None) -> Any:
    cur: Any = data
    for key in path:
        if isinstance(cur, dict):
            if key not in cur:
                return default
            cur = cur[key]
        elif isinstance(cur, list) and isinstance(key, int):
            if key < 0 or key >= len(cur):
                return default
            cur = cur[key]
        else:
            return default
    return cur


html = """
<!DOCTYPE html>
<html><body>
<script id="__NEXT_DATA__" type="application/json">
{
  "props": {
    "pageProps": {
      "product": {
        "sku": "WSA-BOTTLE-750",
        "title": "Insulated Steel Water Bottle 750ml",
        "price": 19.99
      }
    }
  }
}
</script>
</body></html>
"""

soup = BeautifulSoup(html, "html.parser")
tag = soup.find("script", id="__NEXT_DATA__")
next_data = json.loads(tag.string)
print(dig(next_data, "props", "pageProps", "product", "sku"))
# WSA-BOTTLE-750

Appeler le point de terminaison JSON que la page charge en arrière-plan

Ouvrez DevTools → Réseau, rechargez la page et filtrez par Fetch/XHR. De nombreuses interfaces utilisateur de produits et de fiches chargent une URL JSON que le navigateur interroge déjà. Si ce point de terminaison est accessible au public conformément aux conditions d’utilisation du site, vous pouvez l’interroger directement avec les mêmes en-têtes que ceux utilisés par la page et l’analyser avec response.json(). Privilégiez les API documentées ou clairement publiques lorsqu’elles existent. Vérifiez les conditions d’utilisation du site et nos principes relatifs à l’utilisation responsable des données Web avant d’appeler des points de terminaison internes.

Comment analyser le JSON provenant de WebScrapingAPI ?

L’API Scraper peut renvoyer certains champs au format JSON plutôt qu’en HTML brut. Utilisez extract_rules pour les champs nommés, json_response=1 pour l’enveloppe complète (y compris les métadonnées JSON-LD), ou décodez une cible JSON à partir de envelope["body"]. Conservez la clé dans WSA_API_KEY.

Récupérer des champs nommés avec extract_rules

Passez un objet extract_rules à /v2. La réponse contient une propriété par clé de règle. Les règles imbriquées parcourent les cartes répétées ; table_json transforme les tableaux HTML en objets de lignes.

import json
import os

import requests

api_key = os.environ["WSA_API_KEY"]

extract_rules = {
    "title": {"selector": "h1", "output": "text"},
    "results": {
        "selector": "article.product_pod",
        "all": 1,
        "output": {
            "name": {"selector": "h3 a", "output": "@title"},
            "url": {"selector": "h3 a", "output": "@href"},
            "price": {"selector": "p.price_color", "output": "text"},
        },
    },
}

resp = requests.get(
    "https://api.webscrapingapi.com/v2",
    params={
        "api_key": api_key,
        "url": "https://books.toscrape.com/",
        "extract_rules": json.dumps(extract_rules),
    },
    timeout=60,
)
resp.raise_for_status()
fields = resp.json()
print(fields["title"])
# All products
print(fields["results"][:2])
# [{'name': 'A Light in the Attic', 'url': 'catalogue/a-light-in-the-attic_1000/index.html', 'price': '£51.77'}, {'name': 'Tipping the Velvet', 'url': 'catalogue/tipping-the-velvet_999/index.html', 'price': '£53.74'}]

La liste de référence des règles d’extraction répertorie tous les types de sortie, y compris table_json.

Lecture de l’enveloppe de réponse JSON complète

Définir json_response=1 pour encapsuler les données extraites dans un objet JSON avec headers, cost, initial-status-code, body, type, et metadata. L’ metadata objet contient les microdonnées extraites et le JSON-LD ; il s’agit donc metadata["json-ld"] constitue un raccourci lorsque vous n'avez besoin que des données structurées de la page.

import os

import requests

api_key = os.environ["WSA_API_KEY"]

resp = requests.get(
    "https://api.webscrapingapi.com/v2",
    params={
        "api_key": api_key,
        "url": "https://example.com",
        "json_response": 1,
    },
    timeout=60,
)
resp.raise_for_status()
envelope = resp.json()

print(envelope["initial-status-code"], envelope["type"], envelope["cost"])
body = envelope["body"]          # page content as a string
json_ld = envelope.get("metadata", {}).get("json-ld", [])
print(type(body), len(json_ld))

La documentation complète sur la réponse JSON décrit chaque champ de l’enveloppe.

Analyse d’un corps JSON à l’intérieur de l’enveloppe

Lorsque l’URL cible renvoie elle-même du JSON, l’enveloppe place tout de même cette charge utile sous body sous forme de chaîne de caractères. Consultez type (par exemple "json") et décodez-la avec json.loads(envelope["body"]) avant de parcourir les champs.

import json
import os

import requests

api_key = os.environ["WSA_API_KEY"]

resp = requests.get(
    "https://api.webscrapingapi.com/v2",
    params={
        "api_key": api_key,
        "url": "https://httpbin.org/json",
        "json_response": 1,
    },
    timeout=60,
)
resp.raise_for_status()
envelope = resp.json()

if envelope.get("type") == "json":
    payload = json.loads(envelope["body"])
else:
    # HTML or other types still arrive as a string in body
    payload = envelope["body"]

print(type(payload))

Pour les résultats de recherche, une API SERP qui renvoie les résultats sous forme de JSON structuré évite complètement l'analyse du code HTML.

Quelle méthode d’analyse JSON devriez-vous utiliser ?

Adaptez l’outil à vos données : la bibliothèque standard pour les chaînes de caractères et les fichiers, ijson pour les tableaux volumineux, un script de parse pour les données intégrées au code HTML, et une API de scraping lorsque vous souhaitez obtenir des champs ou une structure au lieu du code HTML brut.

Ce dont vous disposez

Que faut-il utiliser

Chaîne JSON ou octets

json.loads

Fichier JSON (UTF-8)

json.load avec encoding="utf-8"

Fichier commençant par un BOM

encoding="utf-8-sig" puis json.load / json.loads

Requêtes / Réponse HTTP

raise_for_status() + Vérification du type de contenu + response.json()

Besoin de valeurs par défaut pour les chemins imbriqués

.get() chaîne ou dig()

Besoin de types / champs obligatoires

Modèle Pydantic ou schéma JSON après analyse

Montants monétaires ou décimales précises

parse_float=Decimal

Les clés en double doivent être rejetées

object_pairs_hook détecteur

Un enregistrement par ligne de scraping

Lignes JSON (*.jsonl)

Tableau trop volumineux pour la RAM

ijson en streaming

Encodage/décodage critique en termes de débit

orjson (mesure sur votre charge utile)

Données intégrées dans du code HTML

JSON-LD / __NEXT_DATA__ / autre script JSON

Champs sélectionnés sur une page via une API

API de scraping Web extract_rules

Besoin des en-têtes, du coût et des métadonnées JSON-LD

json_response=1 enveloppe

L'URL cible renvoie des données JSON via l'API

json.loads(envelope["body"]) lorsque type est json

Foire aux questions

Quelle est la différence entre json.load et json.loads ?

json.loads analyse une chaîne JSON ou des octets déjà en mémoire. json.load lit à partir d’un objet de type fichier et analyse ce flux. Utilisez loads pour les corps d'API et le texte en mémoire ; utilisez load pour les fichiers ouverts avec le bon encodage. Les deux renvoient les mêmes types Python pour le même JSON.

Comment analyser une réponse JSON provenant d'une API en Python ?

Envoyez la requête avec Requests (ou un autre client HTTP), appelez raise_for_status(), vérifiez que le Content-Type ressemble à du JSON, puis appelez response.json() ou json.loads(response.text). Considérez une page HTML vierge (code 200) comme un problème de transport et non comme un problème de syntaxe JSON, et examinez un bref aperçu du corps de la réponse lorsque le décodage échoue.

Pourquoi le message « JSONDecodeError : Expecting value » s'affiche-t-il ?

Le décodeur est arrivé à un endroit où une valeur JSON devrait commencer et n’a rien trouvé de valide : corps vide, JSON tronqué, code HTML ou un token tel que undefined. Affichez e.pos et les caractères environnants. Corrigez la réponse en amont ou le transport ; ne l'ignorez pas avec un simple except.

Comment récupérer en toute sécurité une valeur imbriquée dans un JSON en Python ?

Évitez les chaînes data["a"]["b"] lorsque des clés peuvent être absentes. Utilisez .get("a", {}).get("b") pour les chemins peu profonds, ou une petite dig(data, "a", "b", default=None) fonction d'aide qui accepte également les indices de liste. N'oubliez pas qu'une clé manquante et un JSON explicite null sont deux cas de figure différents.

Comment valider des données JSON en Python ?

L’analyse syntaxique ne vérifie que la syntaxe. Après loads, effectuez une validation à l’aide d’un modèle Pydantic (types, éléments facultatifs, modèles imbriqués) ou d’un document JSON Schema via le jsonschema module. Conservez les champs monétaires sous forme de Decimal dans le modèle lorsque l’arrondi des nombres à virgule flottante serait erroné, et signalez les erreurs de validation à l’appelant.

Comment conserver l'exactitude des prix décimaux lors de l'analyse de JSON ?

Passez parse_float=Decimal à json.loads ou json.load pour que les prix numériques deviennent decimal.Decimal au lieu de float. Lors de l’encodage, sérialisez les décimales sous forme de chaînes de caractères (ou d’entiers représentant les unités mineures) à l’aide d’un default= hook, et veillez à ce que allow_nan=False afin que les constantes numériques non JSON ne puissent pas se glisser dans la sortie.

Comment lire un très gros fichier JSON en Python ?

N’appelez pas json.load sur des tableaux de plusieurs gigaoctets. Utilisez ijson pour parcourir les éléments d’un chemin tel que "items.item" tant que le fichier reste sur le disque. Privilégiez JSON Lines lorsque vous contrôlez le générateur, car chaque ligne est un enregistrement indépendant que vous pouvez traiter en flux et récupérer ligne par ligne.

Qu’est-ce que JSON Lines et quand dois-je l’utiliser ?

JSON Lines (application/jsonl) stocke une valeur JSON par ligne délimitée par un saut de ligne. Il convient aux workflows de scraping et de journalisation où vous ajoutez des enregistrements au fur et à mesure de leur arrivée et les lisez plus tard sans charger le fichier entier. Il ignore les lignes vides et signale le numéro de la ligne en erreur lorsqu’une ligne ne peut pas être décodée.

« orjson » est-il un remplacement direct de « json » ?

Pas tout à fait. orjson est généralement plus rapide, mais dumps les options de retour bytes, les indicateurs d’option remplacent de nombreux arguments-clés, et l’indentation utilise OPT_INDENT_2. Si une bibliothèque attend un str de dumps, ajoutez .decode(). Mesurez les deux sur votre charge utile avant de basculer vers le code de production.

Comment extraire du JSON-LD ou __NEXT_DATA__ à partir d’une page web avec Python ?

__NEXT_DATA__ à partir d’une page web avec Python ?

Analysez le code HTML avec BeautifulSoup, sélectionnez script[type="application/ld+json"] ou script#__NEXT_DATA__, puis exécutez json.loads sur le texte de la balise. Traitez le JSON-LD comme un objet, une liste ou une @graph liste, puis filtrez par @type. Parcourez les propriétés imbriquées avec dig(). Pour les balises rendues côté client, récupérez le code HTML rendu avant de l’analyser.

Comment formater correctement un JSON en Python ?

Appelez json.dumps(data, indent=2, sort_keys=True) pour obtenir une chaîne stable et lisible, ou json.dump(..., indent=2) lors de l’écriture d’un fichier sur le disque. Ajoutez ensure_ascii=False pour le texte non anglais afin que les caractères ne soient pas échappés. Avec orjson, passez option=orjson.OPT_INDENT_2 et décodez les octets renvoyés si vous avez besoin d'une chaîne de caractères.

Obtenez du JSON structuré à partir de n'importe quelle page

Si vous préférez obtenir des champs nommés plutôt que de gérer des sélecteurs CSS, l'API Scraper propose extract_rules et json_response options de l’API Scraper renvoient du JSON que vous pouvez directement intégrer dans json.loads et aux aides ci-dessus. Pour WSA_API_KEY, commencez par la page produit de l’API Scraper et effectuez une validation après chaque analyse.

À propos de l'auteur

Suciu Dan, cofondateur @ WebScrapingAPI

Suciu Dan

cofondateur

Suciu Dan est le cofondateur de WebScrapingAPI et rédige des guides pratiques destinés aux développeurs sur le web scraping avec Python et Ruby, ainsi que sur les infrastructures de proxy.

Web scraping avec AWS Lambda : guide pour Python et Java 2026
Guides

Web scraping avec AWS Lambda : guide pour Python et Java 2026

TL;DR: En bref : le web scraping avec AWS Lambda fonctionne mieux lorsque chaque invocation est courte, bien délimitée et peut faire l'objet d'une nouvelle tentative de manière indépendante. Commencez par utiliser les requêtes HTTP directes, AWS SAM et S3, puis ajoutez SQS, des conteneurs, le rendu dans un navigateur, des proxys ou une couche de récupération gérée uniquement lorsque la charge de travail démontre qu'elle en a besoin.

Suciu Dan33 min read
Lire l'article
Comment utiliser GoSpider : exploration, nettoyage des URL et extraction de données
Guides

Comment utiliser GoSpider : exploration, nettoyage des URL et extraction de données

TL;DR: En bref : GoSpider est un robot d'exploration en ligne de commande destiné à découvrir des URL, et non un outil complet de collecte de données structurées. Ce guide d'utilisation de GoSpider présente comment effectuer une exploration limitée, gérer correctement les résultats, transférer les données de Colly vers un fichier CSV, ainsi qu'une procédure de diagnostic pour les réponses 403 ou les pages nécessitant un rendu JavaScript.

Suciu Dan24 min read
Lire l'article
Comment récupérer les données de Redfin : Guide Python des données immobilières
Guides

Comment récupérer les données de Redfin : Guide Python des données immobilières

TL;DR: Redfin expose des points d'extrémité d'API cachés qui renvoient du JSON structuré pour les listes de propriétés, ce qui permet d'ignorer complètement l'analyse HTML fragile. Ce guide vous accompagne dans la construction d'un scraper Python qui extrait les données de location et de vente, effectue des recherches par emplacement, surveille les nouvelles inscriptions via des sitemaps XML et exporte des résultats propres au format CSV ou JSON.

Suciu Dan15 min read
Lire l'article

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.