Zum Inhalt springen
Zurück zum Blog

So analysieren Sie JSON in Python: Von API-Antworten bis hin zu in Webseiten verstecktem JSON

Suciu DanZuletzt aktualisiert am 12 min read
So analysieren Sie JSON in Python: Von API-Antworten bis hin zu in Webseiten verstecktem JSON
Kurzfassung: Um JSON in Python zu parsen, verwenden Sie das integrierte json-Modul: json.loads() für einen String oder Bytes, json.load() für eine Datei und response.json() von Requests für eine HTTP-Antwort. Prüfen Sie HTTP-Status und JSON-Gültigkeit getrennt, denn auch eine 200-Antwort kann HTML sein. Dieser Leitfaden behandelt außerdem Dekodierungsfehler, verschachtelten Zugriff, Validierung, große Dateien und in Webseiten verstecktes JSON.

Das in Python integrierte json Modul parst JSON. Verwenden Sie json.loads() für eine Zeichenkette oder Byte-Daten, json.load() für eine Datei und die Methode von Requests response.json() für eine HTTP-Antwort. Dieser Leitfaden behandelt Dekodierungsfehler, den sicheren Zugriff auf verschachtelte Daten, die Validierung, große Dateien, die Genauigkeit von Zahlen sowie in Webseiten eingebettetes JSON.

Das Wichtigste auf einen Blick

  • Verwenden Sie vorzugsweise json.loads für Zeichenfolgen/Bytes und json.load für Dateien; öffnen Sie Dateien mit UTF-8 (oder utf-8-sig , wenn ein BOM vorhanden ist).
  • Behandeln Sie den HTTP-Status und die JSON-Gültigkeit als separate Prüfungen: Eine 200-Antwort kann dennoch HTML sein.
  • Navigiere durch unübersichtliche, verschachtelte Daten mit .get() Ketten oder einem kleinen dig() Hilfsprogramm; validieren Sie nach dem Parsen mit Pydantic oder JSON Schema.
  • Streamen Sie riesige Arrays mit ijson, fügen Sie Scraping-Läufe als JSON-Zeilen an und messen Sie orjson anhand Ihrer eigenen Payloads.
  • Bevorzugen Sie in die Seite eingebettetes JSON (JSON-LD, __NEXT_DATA__) oder die strukturierten Felder einer Scraping-API gegenüber instabilen CSS-Selektoren, wenn sich das HTML-Layout häufig ändert.

Wie parst man eine JSON-Zeichenkette in Python?

Rufen Sie json.loads() auf eine str oder bytes Wert auf. Es gibt ein Python-Objekt zurück – in der Regel ein dict oder list–, das die JSON-Struktur widerspiegelt.

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"]

Es wird durchgehend dieselbe Produktliste verwendet, sodass jedes Beispiel auf einem im Scraping-Stil erstellten Datensatz aufbaut.

Wie liest man eine JSON-Datei in Python ein?

Öffnen Sie die Datei mit UTF-8-Kodierung und übergeben Sie das Dateiobjekt an 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

Was tun, wenn eine JSON-Datei mit einem BOM beginnt?

Manche Editoren speichern UTF-8 mit einer Byte-Order-Mark (EF BB BF). Dieses unsichtbare Zeichen steht vor dem ersten { und verhindert eine normale Dekodierung. Öffnen Sie die Datei mit encoding="utf-8-sig" , damit Python die BOM vor der json.load ausgeführt wird. Wenn Sie bereits über Bytes verfügen, dekodieren Sie diese mit utf-8-sig und rufen Sie anschließend 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"))

Wie JSON-Typen Python-Typen zugeordnet werden

JSON-Typen werden gemäß der JSON-Grammatik auf json.org und der Standardbibliothek auf Python-Typen abgebildet:

JSON

Python (json.loads)

Python → JSON (json.dumps)

Objekt

dict

dict

Array

list

list / tuple

Zeichenkette

str

str

Zahl (int)

int

int

Zahl (real)

float

float / int

true / false

True / False

bool

null

None

None

Warum sich Ihre Schlüssel und Tupel nach einem Roundtrip ändern

JSON-Objektnamen sind immer Zeichenketten. Ganzzahlige Wörterbuchschlüssel werden nach dem dumps → loads. Tupel werden zu Listen, da JSON nur Arrays kennt. Mengen sind nicht serialisierbar und lösen einen Fehler aus 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

Wie analysiert man eine JSON-Antwort von einer API?

Rufen Sie response.raise_for_status() für HTTP-Fehler auf, und response.json() um den Hauptteil zu dekodieren. Statuscodes und die Gültigkeit des JSON-Datensatzes sind voneinander unabhängig: Ein erfolgreicher Statuscode garantiert noch keinen gültigen JSON-Hauptteil.

import requests

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

Wenn eine 200-Antwort kein JSON ist

Block- und Challenge-Seiten geben oft HTML mit dem HTTP-Status 200 zurück. Überprüfen Sie Content-Type, fangen Sie Dekodierungsfehler ab und zeigen Sie eine kurze, sichere Vorschau des Hauptteils an:

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

Eine HTML-Seite mit dem Status 200 ist oft eine Sperrseite; in unserem Leitfaden zum Scraping ohne Sperrung wird erklärt, wie man dies vermeiden kann.

Wie behebt man einen „JSONDecodeError“?

JSONDecodeError bedeutet, dass die Eingabe kein gültiges JSON ist: leerer Hauptteil, abgeschnittene Nutzdaten, HTML, einfache Anführungszeichen, ein abschließendes Komma oder Ähnliches. Lesen Sie e.msg, e.lineno, e.colnound e.pos, und geben Sie dann den Text um pos aus, damit du die Fehlerstelle erkennen kannst. Ausführliche Informationen zu Attributen und Hooks findest du in der Dokumentation des Python-Moduls „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

Häufige Ursachen und wie diese jeweils aussehen

Symptom (Meldungsfragment)

Mögliche Ursache

Behebung

Expecting value an Position 0

Leerer Textkörper oder HTML/undefined

Überprüfen Sie den Transport; stellen Sie sicher, dass Sie JSON

Expecting value in der Mitte der Zeichenfolge

Abgeschnittene Antwort

Wiederholen, Timeouts erhöhen oder mit Längenprüfungen streamen

Expecting property name enclosed in double quotes

Schlüssel in einfachen Anführungszeichen oder abschließendes Komma

Korrigieren Sie den Produzenten; „reparieren“ Sie nicht mit eval

Extra data

Zwei JSON-Werte in einer Zeichenkette

JSON-Zeilen aufteilen oder jeweils einen Wert nach dem anderen analysieren

HTML-Tags in der Vorschau

Soft-Block oder Anmeldeseite

Korrigiere Header, Cookies oder die Darstellung; siehe den Leitfaden zur Fehlerbehebung oben

Wie greift man sicher auf verschachteltes JSON zu?

Harte Indizierung (data["seller"]["rating"]) löst KeyError , wenn ein Schlüssel fehlt. .get() Ketten geben einen Standardwert zurück, verursachen jedoch bei tiefen Pfaden viel „Lärm“. Ein kleiner typisierter dig() Helper durchläuft Dict-Schlüssel und Listenindizes und behandelt einen fehlenden Pfad anders als einen expliziten 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

Wie validiert man JSON-Daten in Python?

Das Parsen überprüft lediglich die Syntax. Nach dem loadssollten Sie Typen und Pflichtfelder mit Pydantic oder JSON Schema überprüfen.

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)")

Die gleiche Struktur wie ein JSON-Schema, validiert mit dem jsonschema Paket validiert (siehe auch „JSON Schema verstehen“):

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,
)

Wie schreibt man JSON in eine Zeichenkette oder eine Datei?

Verwenden Sie json.dumps für eine Zeichenkette und json.dump für eine Datei. indent formatiert, sort_keys=True gleicht Unterschiede aus und separators=(",", ":") verkleinert die Datenmengen.

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)

Schreiben von nicht-englischem Text ohne Escape-Sequenzen

Standardmäßig dumps werden Nicht-ASCII-Zeichen als \uXXXX. Geben Sie ensure_ascii=False , wenn Sie lesbares UTF-8 in Dateien und Protokollen wünschen.

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"}

Serialisierung von `datetime`, `Decimal` und Datenklassen

Übergeben Sie eine default= Funktion für Typen, die der Encoder nicht kennt. Sie können auch eine Unterklasse erstellen json.JSONEncoder , wenn Sie eine wiederverwendbare Encoder-Klasse benötigen.

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"}

Wie gewährleistet man die Genauigkeit von Preisen und großen Zahlen?

Verwenden Sie parse_float=Decimal , damit Werte wie 19.99 als Dezimalzahlen statt als binäre Gleitkommazahlen exakt bleiben. RFC 8259, der JSON-Standard, erlaubt NaN oder Infinity. Nach dem Parsen ist das Python-Modul „decimal“ der übliche Ort für geldsichere Werte.

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)

Pythons Decoder akzeptiert NaN und Infinity standardmäßig als Erweiterung. Übergeben Sie ein parse_constant , das einen Fehler auslöst, wenn Sie diese bei der Eingabe ablehnen möchten.

Wie erkennt man doppelte Schlüssel in JSON?

RFC 8259, der JSON-Standard, besagt, dass Objektnamen eindeutig sein sollten, aber json.loads behält den letzten Wert stillschweigend bei. Ein object_pairs_hook kann stattdessen eine Ausnahme auslösen.

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'

Wie arbeitet man mit JSON-Lines-Dateien?

Das JSON-Lines-Format speichert einen JSON-Wert pro Zeile. Füge nach jedem Scrape einen Datensatz an, lies dann Zeile für Zeile, überspringe Leerzeichen und gib die Zeilennummer aus, wenn ein Datensatz nicht dekodiert werden kann.

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")

Wie analysiert man eine JSON-Datei, die zu groß für den Arbeitsspeicher ist?

Verwenden Sie „ijson“ auf PyPI: Durchlaufen Sie Elemente unter einem Pfad, anstatt das gesamte Dokument zu laden. Verwenden Sie use_float=True nur dort, wo binäre Gleitkommagenauigkeit akzeptabel ist.

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)

Ist „orjson“ schneller als das „json“-Modul?

Oft ja, aber messen Sie es anhand Ihrer eigenen Daten. Die Dokumentation von orjson auf PyPI weist auf die API-Unterschiede hin, die in der Praxis von Bedeutung sind: dumps gibt bytes, Optionen sind Flags statt Schlüsselwortargumente, und bei der Einrückung wird OPT_INDENT_2 anstelle eines indent Ganzzahl.

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.

Wie extrahiert man in einer Webseite versteckte JSON-Daten?

Viele Seiten transportieren ihre Daten als JSON in <script> -Tags, was oft stabiler ist als CSS-Selektoren auf gerendertem HTML. Analysieren Sie dieses JSON und durchlaufen Sie es anschließend mit denselben Tools, die Sie für API-Antworten verwenden.

Lesen von strukturierten JSON-LD-Daten

JSON-LD befindet sich in script[type="application/ld+json"] Blöcken. Eine Seite kann ein einzelnes Objekt, eine Liste oder einen @graph Wrapper ausgeben. Filtern Sie nach @type == "Product" (oder nach dem Typ, den Sie benötigen):

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"])

Wenn eine Seite ihr JSON-LD per JavaScript einbindet, liefert dir ein gehosteter Browser, der die Seite rendert, bevor er sie zurückgibt, den endgültigen HTML-Code.

Das Auslesen __NEXT_DATA__ und anderer Framework-Zustände

__NEXT_DATA__ und anderer Framework-Zustände

Next.js und ähnliche Frameworks betten die Seiten-Props in ein script#__NEXT_DATA__ Tag ein. Analysieren Sie den Tag-Text und durchlaufen Sie ihn anschließend mit dig(). Eine vollständige Anleitung für den Einzelhandel finden Sie in unserem Leitfaden zum Scraping von Walmart, in dem der Produktdatensatz von __NEXT_DATA__ analysiert wird.

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

Aufruf des JSON-Endpunkts, den eine Seite im Hintergrund lädt

Öffne DevTools → Netzwerk, lade die Seite neu und filtere nach „Fetch/XHR“. Viele Produkt- und Angebots-Benutzeroberflächen laden eine JSON-URL, die der Browser bereits aufruft. Wenn dieser Endpunkt gemäß den Nutzungsbedingungen der Website öffentlich erreichbar ist, kannst du ihn direkt mit denselben Headern anfordern, die die Seite verwendet, und ihn mit response.json(). Bevorzugen Sie dokumentierte oder eindeutig öffentliche APIs, sofern diese vorhanden sind. Prüfen Sie die Nutzungsbedingungen der Website und unsere Grundsätze für verantwortungsvollen Umgang mit Webdaten, bevor Sie interne Endpunkte aufrufen.

Wie analysiert man JSON aus der WebScrapingAPI?

Die Scraper-API kann ausgewählte Felder als JSON anstelle von Roh-HTML zurückgeben. Verwenden Sie extract_rules für benannte Felder, json_response=1 für den vollständigen Envelope (einschließlich JSON-LD-Metadaten) oder dekodieren Sie ein JSON-Ziel aus envelope["body"]. Behalten Sie den Schlüssel in WSA_API_KEY.

Benannte Felder mit `extract_rules` abrufen

Übergeben Sie ein in eine JSON-Zeichenkette umgewandeltes extract_rules Objekt an /v2. Die Antwort enthält eine Eigenschaft pro Regelschlüssel. Verschachtelte Regeln durchlaufen wiederholte Karten; table_json wandelt HTML-Tabellen in Zeilenobjekte um.

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'}]

Die Referenzliste der Extraktionsregeln listet alle Ausgabetypen auf, einschließlich table_json.

Lesen des vollständigen JSON-Antwort-Envelopes

Einstellung json_response=1 , um das Scraping-Ergebnis in ein JSON-Objekt mit headers, cost, initial-status-code, body, type, und metadata. Das metadata Objekt enthält extrahierte Mikrodaten und JSON-LD, daher metadata["json-ld"] ist eine Abkürzung, wenn Sie nur strukturierte Daten von der Seite benötigen.

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))

In der vollständigen Dokumentation zur JSON-Antwort werden die einzelnen Felder des „Envelope“ beschrieben.

Parsen eines JSON-Hauptteils innerhalb des „Envelope“

Wenn die Ziel-URL selbst JSON zurückgibt, fügt der Envelope diese Nutzdaten dennoch body als Zeichenkette ein. Siehe type (zum Beispiel "json") und dekodieren Sie mit json.loads(envelope["body"]) , bevor Sie die Felder durchlaufen.

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))

Bei Suchergebnissen überspringt eine SERP-API, die Ergebnisse als strukturiertes JSON zurückgibt, das HTML-Parsing vollständig.

Welche JSON-Parsing-Methode sollten Sie verwenden?

Passen Sie die Eingabe an das jeweilige Tool an: die Standardbibliothek für Zeichenfolgen und Dateien, ijson für riesige Arrays, ein Skript zum Parsen von in HTML eingebetteten Daten und eine Scraping-API, wenn Sie Felder oder einen Envelope anstelle von rohem HTML benötigen.

Was Sie haben

Was du verwenden solltest

JSON-Zeichenkette oder Bytes

json.loads

JSON-Datei (UTF-8)

json.load mit encoding="utf-8"

Datei, die mit einem BOM beginnt

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

Anfragen / HTTP-Antwort

raise_for_status() + Überprüfung des Content-Types + response.json()

Standardwerte für verschachtelte Pfade erforderlich

.get() Kette oder dig()

Typen / Pflichtfelder erforderlich

Pydantic-Modell oder JSON-Schema nach dem Parsen

Geldbeträge oder exakte Dezimalzahlen

parse_float=Decimal

Doppelte Schlüssel müssen fehlschlagen

object_pairs_hook Detektor

Ein Datensatz pro Scrape-Zeile

JSON-Zeilen (*.jsonl)

Array zu groß für den Arbeitsspeicher

ijson-Streaming

Durchsatzkritische Kodierung/Dekodierung

orjson (Messen Sie anhand Ihrer Nutzdaten)

In HTML eingebettete Daten

JSON-LD / __NEXT_DATA__ / Sonstiges script JSON

Ausgewählte Felder einer Seite über API

WebScrapingAPI extract_rules

Benötigt: Kopfzeilen, Kosten, JSON-LD-Metadaten

json_response=1 envelope

Die Ziel-URL gibt über die API JSON zurück

json.loads(envelope["body"]) wenn type ist json

Häufig gestellte Fragen

Was ist der Unterschied zwischen „json.load“ und „json.loads“?

json.loads analysiert eine JSON-Zeichenkette oder Bytes, die sich bereits im Speicher befinden. json.load Liest aus einem dateiähnlichen Objekt und analysiert diesen Datenstrom. Verwenden Sie loads für API-Body-Daten und Text im Arbeitsspeicher; verwende load für Dateien, die mit der richtigen Kodierung geöffnet wurden. Beide geben für denselben JSON-Inhalt dieselben Python-Typen zurück.

Wie parse ich eine JSON-Antwort von einer API in Python?

Senden Sie die Anfrage mit Requests (oder einem anderen HTTP-Client), rufen Sie raise_for_status(), überprüfen Sie, ob der Content-Type wie JSON aussieht und rufen Sie anschließend response.json() oder json.loads(response.text)auf. Betrachten Sie eine leere HTML-Seite mit dem Status 200 als Transportproblem und nicht als Problem mit der JSON-Syntax, und überprüfen Sie eine kurze Vorschau des Hauptteils, wenn die Dekodierung fehlschlägt.

Warum erhalte ich die Fehlermeldung „JSONDecodeError: Expecting value“?

Der Decoder ist an einer Stelle angelangt, an der ein JSON-Wert beginnen sollte, und hat nichts Gültiges gefunden – leerer Body, abgeschnittenes JSON, HTML oder ein Token wie undefined. Gib e.pos und die umgebenden Zeichen aus. Beheben Sie die Ursache in der vorgelagerten Antwort oder im Transport; ignorieren Sie den Fehler nicht einfach mit einem pauschalen except.

Wie kann ich in Python sicher einen verschachtelten Wert aus JSON abrufen?

Vermeiden Sie verkettete data["a"]["b"] , wenn Schlüssel möglicherweise fehlen. Verwenden Sie .get("a", {}).get("b") für flache Pfade oder eine kleine dig(data, "a", "b", default=None) Hilfsfunktion, die auch Listenindizes akzeptiert. Beachten Sie, dass ein fehlender Schlüssel und ein explizites JSON null unterschiedliche Ergebnisse sind.

Wie validiere ich JSON-Daten in Python?

Beim Parsen wird nur die Syntax überprüft. Nach dem loadssollten Sie die Daten mit einem Pydantic-Modell (Typen, Optionals, verschachtelte Modelle) oder einem JSON-Schema-Dokument über das jsonschema Paket. Behalten Sie Geldfelder als Decimal im Modell, wenn eine Rundung von Float-Werten zu falschen Ergebnissen führen würde, und geben Sie Validierungsfehler an den Aufrufer weiter.

Wie stelle ich sicher, dass Dezimalpreise beim Parsen von JSON exakt bleiben?

Übergeben Sie parse_float=Decimal an json.loads oder json.load damit numerische Preise decimal.Decimal statt float. Bei der Kodierung sollten Dezimalzahlen als Zeichenfolgen (oder als Ganzzahlen der Untereinheiten) mit einem default= Hook und stelle sicher, dass allow_nan=False damit keine nicht-JSON-konformen numerischen Konstanten in die Ausgabe gelangen können.

Wie lese ich eine sehr große JSON-Datei in Python ein?

Rufen Sie json.load bei Arrays mit mehreren Gigabyte. Verwenden Sie `ijson`, um Elemente unter einem Pfad wie "items.item" während die Datei auf der Festplatte verbleibt. Bevorzugen Sie JSON Lines, wenn Sie die Kontrolle über den Erzeuger haben, da jede Zeile einen eigenständigen Datensatz darstellt, den Sie zeilenweise streamen und wiederherstellen können.

Was ist „JSON Lines“ und wann sollte ich es verwenden?

JSON Lines (application/jsonl) speichert einen JSON-Wert pro durch einen Zeilenumbruch getrennter Zeile. Es eignet sich für Scraping- und Logging-Workflows, bei denen Sie Datensätze bei ihrem Eintreffen anhängen und später lesen, ohne die gesamte Datei zu laden. Leere Zeilen werden übersprungen, und die Nummer der fehlerhaften Zeile wird gemeldet, wenn eine Zeile nicht dekodiert werden kann.

Ist „orjson“ ein direkter Ersatz für „json“?

Nicht ganz. orjson ist in der Regel schneller, aber dumps gibt bytes, Optionsflags ersetzen viele Schlüsselwortargumente, und die Einrückung erfolgt mit OPT_INDENT_2. Wenn eine Bibliothek ein str von dumps, fügen Sie .decode()hin. Messen Sie beides anhand Ihrer Nutzdaten, bevor Sie den Produktionscode umstellen.

Wie extrahiere ich JSON-LD oder __NEXT_DATA__ aus einer Webseite mit Python extrahieren?

__NEXT_DATA__ aus einer Webseite mit Python extrahieren?

Analysieren Sie den HTML-Code mit BeautifulSoup, wählen Sie script[type="application/ld+json"] oder script#__NEXT_DATA__aus und führen Sie json.loads auf den Tag-Text anwenden. Behandeln Sie JSON-LD als Objekt, Liste oder @graph Liste und filtern Sie anschließend nach @type. Durchlaufen Sie verschachtelte Props mit dig(). Bei clientseitig gerenderten Tags den gerenderten HTML-Code vor dem Parsen abrufen.

Wie kann ich JSON in Python übersichtlich ausgeben?

Rufe json.dumps(data, indent=2, sort_keys=True) auf, um eine stabile, lesbare Zeichenkette zu erhalten, oder json.dump(..., indent=2) beim Schreiben einer Datei auf die Festplatte. Fügen Sie ensure_ascii=False für nicht-englischen Text hinzu, damit Zeichen nicht escaped werden. Bei der Verwendung von orjson übergeben Sie option=orjson.OPT_INDENT_2 und dekodieren Sie die zurückgegebenen Bytes, wenn Sie eine Zeichenkette benötigen.

Strukturiertes JSON von jeder Seite abrufen

Wenn du lieber benannte Felder abrufen möchtest, anstatt CSS-Selektoren zu verwalten, bietet die Scraper-API extract_rules und json_response Optionen der Scraper-API JSON zurück, das Sie direkt in json.loads und die oben genannten Hilfsfunktionen einspeisen können. Beginnen Sie WSA_API_KEY, beginnen Sie auf der Produktseite der Scraper-API und führen Sie nach jedem Parsing eine Validierung durch.

Über den Autor

Suciu Dan, Mitbegründer @ WebScrapingAPI

Suciu Dan

Mitbegründer

Suciu Dan ist Mitbegründer von WebScrapingAPI und verfasst praxisorientierte, auf Entwickler zugeschnittene Anleitungen zu den Themen Web-Scraping mit Python, Web-Scraping mit Ruby und Proxy-Infrastruktur.

Web-Scraping mit AWS Lambda: Leitfaden für Python und Java 2026
Anleitungen

Web-Scraping mit AWS Lambda: Leitfaden für Python und Java 2026

TL;DR: Kurz gesagt: Web-Scraping mit AWS Lambda funktioniert am besten, wenn jeder Aufruf kurz, begrenzt und unabhängig wiederholbar ist. Beginnen Sie mit direktem HTTP, AWS SAM und S3 und fügen Sie erst dann SQS, Container, Browser-Rendering, Proxys oder eine verwaltete Abrufebene hinzu, wenn sich im Rahmen der Arbeitslast herausstellt, dass diese erforderlich sind.

Suciu Dan26 min read
Artikel lesen
So verwenden Sie GoSpider: Crawlen, URLs bereinigen und Daten extrahieren
Anleitungen

So verwenden Sie GoSpider: Crawlen, URLs bereinigen und Daten extrahieren

TL;DR: GoSpider ist ein Befehlszeilen-Crawler zum Auffinden von URLs, kein vollständiger Scraper für strukturierte Daten. Diese Anleitung zur Verwendung von GoSpider zeigt einen begrenzten Crawl, die saubere Verarbeitung der Ausgabe, die Übergabe von Colly an CSV sowie einen Diagnosepfad für 403-Antworten oder Seiten, die eine JavaScript-Darstellung erfordern.

Suciu Dan20 min read
Artikel lesen
Wie man Redfin scrappt: Python-Leitfaden für Immobiliendaten
Anleitungen

Wie man Redfin scrappt: Python-Leitfaden für Immobiliendaten

TL;DR: Redfin stellt versteckte API-Endpunkte zur Verfügung, die strukturiertes JSON für Immobilienangebote zurückgeben, wodurch das fragile HTML-Parsing vollständig übersprungen werden kann. Diese Anleitung führt Sie durch den Aufbau eines Python-Scrapers, der Miet- und Verkaufsdaten extrahiert, nach Standort sucht, neue Angebote über XML-Sitemaps überwacht und saubere Ergebnisse in CSV oder JSON exportiert.

Suciu Dan11 min read
Artikel lesen

Los geht’s

Sind Sie bereit, Ihre Datenerfassung zu erweitern?

Schließen Sie sich den über 2.000 Unternehmen an, die WebScrapingAPI nutzen, um Webdaten im Unternehmensmaßstab ohne zusätzlichen Infrastrukturaufwand zu extrahieren.