🎯 Um navegador em nuvem personalizável e anti-detecção alimentado por Chromium desenvolvido internamente, projetado para rastreadores web e agentes de IA. 👉Experimente agora
De volta ao blog

JSON-LD Web Scraping Com Python: Extraia Dados Estruturados

Emily Chen
Emily Chen

Advanced Data Extraction Specialist

30-Jul-2026

TL;DR:

  • JSON-LD geralmente é o caminho mais curto de uma página para um registro tipado. Procure por <script type="application/ld+json"> antes de escrever seletores para título, autor, data de publicação, imagem ou campos de produto.
  • Leia cada bloco JSON-LD. Uma página pode dividir dados de organização, breadcrumb, artigo, produto e FAQ em vários scripts.
  • Normalize três formas de nível superior. Um bloco JSON-LD pode ser um objeto, um array de objetos, ou um objeto cujo @graph contém os nós úteis.
  • Os campos do Schema.org são opcionais na prática. Selecione o nó pelo @type, mantenha campos ausentes como None e valide apenas os campos que seu pipeline realmente exige.
  • Beautiful Soup não executa JavaScript. Se uma página injeta JSON-LD após a carga, busque o HTML renderizado primeiro e execute o mesmo parser nessa resposta.
  • Você pode testar o parser sem um modelo na nuvem. O exemplo completo em Python abaixo lê um nó Article real, compara seu título com o H1 visível e valida a saída.
  • Comece com páginas públicas e delimitadas. Crie uma conta gratuita no Scrapeless quando seu alvo precisar de HTML renderizado.

JSON-LD frequentemente contém os campos que um scraper está prestes a reconstruir a partir de elementos de página dispersos. Em um artigo live do Scrapeless, um único objeto Article carrega o título, autor, editora, datas, URL canônica, imagem principal, descrição e palavras-chave. A página visível ainda importa, mas os metadados estruturados fornecem um ponto de partida tipado para o pipeline de extração.

JSON-LD segue a especificação JSON-LD 1.1, enquanto vocabulários como Article, Product e BreadcrumbList vêm do modelo de dados estruturados Schema.org. Nenhum padrão garante que cada editor preencha cada propriedade. Seu parser deve preservar essa incerteza em vez de inventar valores.

Para que o Web Scraping JSON-LD é Bom

JSON-LD é útil quando uma página publica fatos legíveis por máquina ao lado de seu layout voltado para humanos. Nós comuns incluem:

  • Article e NewsArticle para títulos, datas, autores, imagens e editores;
  • Product para nomes, marcas, ofertas, avaliações e identificadores;
  • BreadcrumbList para hierarquia e caminhos de categoria canônicos;
  • Organization, Person e LocalBusiness para metadados de entidade;
  • VideoObject, Recipe, Event, e outros tipos específicos de domínio.

O bloco de script não é um endpoint privado. Ele é parte da resposta da página e é destinado a máquinas como crawlers de busca. Isso o torna mais durável do que uma classe CSS gerada, mas não automaticamente completo ou correto. Trate-o como uma fonte para validação, não como um oráculo.

Instalar Beautiful Soup

Este guia usa Python 3.10 ou posterior, requests e beautifulsoup4 4.15.0:

bash Copy
python -m pip install "requests>=2.32,<3" "beautifulsoup4==4.15.0"

A API de busca de árvore do Beautiful Soup pode filtrar tags por atributo, o que é suficiente para coletar todos os scripts correspondentes. A decodificação JSON permanece na biblioteca padrão do Python.

Buscar o HTML Fonte

Comece com uma solicitação HTTP ordinária. O alvo usado aqui publica seu JSON-LD na resposta inicial, portanto, a renderização JavaScript adicionaria custo sem mudar o resultado:

python Copy
import requests

URL = "https://www.scrapeless.com/pt/blog/what-is-web-scraping?utm_source=website&utm_medium=blog&utm_campaign=universalscrapingapi&utm_term=json-ld-structured-data-web-scraping"

response = requests.get(
    URL,
    headers={"User-Agent": "Mozilla/5.0"},
    timeout=30,
)
response.raise_for_status()
print("Bytes HTML:", len(response.content))
text Copy
Bytes HTML: 439487

A contagem de bytes pode mudar quando o pacote da página muda. O invariante útil é que a resposta contém pelo menos um script application/ld+json decodificável.

Analisar Cada Bloco JSON-LD

Não use soup.find(...) a menos que o contrato da página prometa explicitamente um bloco. find_all preserva a possibilidade de que o artigo, breadcrumb e editor vivam em scripts separados:

python Copy
import json
from bs4 import BeautifulSoup

soup = BeautifulSoup(response.text, "html.parser")
scripts = soup.find_all("script", type="application/ld+json")

parsed_blocks = []
for script in scripts:
    raw = script.get_text(strip=True)
    try:
        parsed_blocks.append(json.loads(raw))
    except json.JSONDecodeError:
        continue

print("Blocos JSON-LD:", len(scripts))
print("blocos decodificados:", len(parsed_blocks))

Pular blocos malformados é seguro apenas quando você também registra quantos foram pulados. Um except silencioso pode transformar uma regressão de metadados em um conjunto de dados vazio que parece bem-sucedido.

Normalizar Objetos, Arrays e @graph

Autores de JSON-LD têm várias maneiras válidas de agrupar nós. Este gerador achata as três formas que um scraper encontra com mais frequência:

python Copy
def iter_nodes(value):
    if isinstance(value, list):
        for item in value:
            yield from iter_nodes(item)
    elif isinstance(value, dict):
        graph = value.get("@graph")
        if isinstance(graph, list):
            for item in graph:
                yield from iter_nodes(item)
        else:
            yield value

nodes = [node for block in parsed_blocks for node in iter_nodes(block)]
article = next(node for node in nodes if node.get("@type") == "Article")

print("nós normalizados:", len(nodes))
print("tipo selecionado:", article["@type"])

@type também pode ser um array. Se seu corpus incluí editores que emitem "@type": ["Article", "NewsArticle"], normalize esse campo antes de testar a afiliação.

Construir um Registro Nullable

Objetos aninhados precisam da mesma atenção que campos de nível superior. O autor pode ser um dicionário, uma lista, uma string ou não estar presente. Este alvo usa um dicionário, então o exemplo o lê defensivamente e preserva campos opcionais ausentes como None:

python Copy
visible_h1 = soup.find("h1").get_text(" ", strip=True)
author = article.get("author") or {}
publisher = article.get("publisher") or {}

record = {
    "type": article.get("@type"),
    "headline": article.get("headline"),
    "visible_h1": visible_h1,
    "author": author.get("name") if isinstance(author, dict) else None,
    "publisher": publisher.get("name") if isinstance(publisher, dict) else None,
    "published": article.get("datePublished"),
    "modified": article.get("dateModified"),
    "image": article.get("image"),
    "description": article.get("description"),
    "keywords": article.get("keywords"),
    "source_url": URL,
}

Manter source_url em cada linha torna possíveis auditorias posteriores. Sem a proveniência, um parser corrigido não consegue identificar quais registros precisam ser reconstruídos.

Validar os Campos que Seu Pipeline Requer

A validação deve refletir o contrato de downstream, e não cada propriedade que o Schema.org permite:

python Copy
required = ("headline", "author", "published", "source_url")
missing = [field for field in required if not record.get(field)]
if missing:
    raise ValueError(f"campos obrigatórios ausentes: {missing}")

print("cabeçalho:", record["headline"])
print("H1 visível:", record["visible_h1"])
print("cabeçalhos coincidem:", record["headline"] == record["visible_h1"])
print("autor:", record["author"])
print("editor:", record["publisher"])
print("publicado:", record["published"])
text Copy
cabeçalho: O que é Web Scraping? Guia Definitivo 2025
H1 visível: O que é Web Scraping? Guia Definitivo 2025
cabeçalhos coincidem: True
autor: Emily Chen
editor: Scrapeless
publicado: 2025-09-17T08:35:31.224Z

O cabeçalho coincide nesta página. Não generalize esse resultado: editores às vezes atualizam o H1 visível sem atualizar os metadados estruturados ou usam um cabeçalho de pesquisa mais curto em JSON-LD. Comparar ambas as superfícies é um verificador de qualidade útil.

Pronto para executar o mesmo parser em uma resposta renderizada? Abra uma conta no Scrapeless e mantenha o código de parsing inalterado.

Extrator Completo Executável

O script completo une descoberta, normalização, seleção, mapeamento nullable e validação:

python Copy
import json
import requests
from bs4 import BeautifulSoup

URL = "https://www.scrapeless.com/pt/blog/what-is-web-scraping?utm_source=website&utm_medium=blog&utm_campaign=universalscrapingapi&utm_term=json-ld-structured-data-web-scraping"


def iter_nodes(value):
    if isinstance(value, list):
        for item in value:
            yield from iter_nodes(item)
    elif isinstance(value, dict):
        graph = value.get("@graph")
        if isinstance(graph, list):
            for item in graph:
                yield from iter_nodes(item)
        else:
            yield value


response = requests.get(
    URL,
    headers={"User-Agent": "Mozilla/5.0"},
    timeout=30,
)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")

nodes = []
invalid_blocks = 0
scripts = soup.find_all("script", type="application/ld+json")
for script in scripts:
    try:
        nodes.extend(iter_nodes(json.loads(script.get_text(strip=True))))
    except json.JSONDecodeError:
        invalid_blocks += 1

article = next(node for node in nodes if node.get("@type") == "Article")
author = article.get("author") or {}
publisher = article.get("publisher") or {}
visible_h1 = soup.find("h1").get_text(" ", strip=True)

record = {
    "type": article.get("@type"),
    "headline": article.get("headline"),
json Copy
"visible_h1": visible_h1,
    "autor": autor.get("nome") if isinstance(autor, dict) else None,
    "editor": editor.get("nome") if isinstance(editor, dict) else None,
    "publicado": artigo.get("dataPublicada"),
    "imagem": artigo.get("imagem"),
    "palavras-chave": artigo.get("palavras-chave"),
    "url_fonte": URL,
}

requerido = ("cabeçalho", "autor", "publicado", "url_fonte")
faltando = [campo for campo in requerido if not registro.get(campo)]
if faltando:
    raise ValueError(f"faltando campos obrigatórios: {faltando}")

print(f"Bytes HTML: {len(resposta.content)}")
print(f"Blocos JSON-LD: {len(scripts)}")
print(f"nós normalizados: {len(nos)}")
print(f"blocos inválidos: {blocos_invalidos}")
print(f"tipo: {registro['tipo']}")
print(f"cabeçalho: {registro['cabeçalho']}")
print(f"visible H1: {registro['visible_h1']}")
print(f"cabeçalhos correspondem: {registro['cabeçalho'] == registro['visible_h1']}")
print(f"autor: {registro['autor']}")
print(f"editor: {registro['editor']}")
print(f"publicado: {registro['publicado']}")
print(f"caracteres das palavras-chave: {len(registro['palavras-chave'] or '')}")

A execução ao vivo retornou um bloco válido, um nó `Artigo` normalizado, nenhum bloco malformado, cabeçalhos correspondentes, autor `Emily Chen`, editor `Scrapeless`, e 140 caracteres na string de palavras-chave.

## Quando o JSON-LD Aparece Apenas Após Renderização

Beautiful Soup analisa os bytes que recebe; não executa o JavaScript de uma página. Um diagnóstico rápido é comparar a resposta bruta com o DOM do navegador. Se o navegador mostrar um script `application/ld+json`, mas `requests` não encontrar nenhum, busque o HTML renderizado antes de analisar.

> Nota: A solicitação abaixo requer uma conta Scrapeless financiada. A conta de verificação retornou uma resposta de saldo insuficiente durante a verificação final, portanto, essa chamada HTTP é uma lacuna pré-requisito; o parser acima foi executado completamente contra a página pública real.

```python
import os
import requests

renderizado = requests.post(
    "https://api.scrapeless.com/api/v2/unlocker/request",
    headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"]},
    json={
        "ator": "unlocker.webunlocker",
        "entrada": {
            "url": URL,
            "método": "GET",
            "js_render": True,
        },
    },
    timeout=90,
)
renderizado.raise_for_status()
html = renderizado.json()["data"]

Passe html para BeautifulSoup e reutilize o mesmo normalizador. A API Universal de Scraping fornece a resposta renderizada; não altera o contrato JSON-LD.

Problemas Comuns de Dados JSON-LD

A página tem vários nós correspondentes

Selecione por tipo e identidade. Para variantes de produto, use @id, URL, SKU ou outro campo estável em vez de pegar o primeiro nó Produto.

@type é um array

Converta-o em um conjunto antes de verificar a associação. Um teste de igualdade estrita contra uma string perderá um nó multi-tipo válido.

O script contém entidades HTML ou comentários

JSON-LD deve ser texto JSON válido. Se um editor envolvê-lo em uma sintaxe inválida, registre esse bloco como malformado e conserte o parser para aquela fonte conhecida; não aplique substituições de string amplas que possam corromper valores legítimos.

Metadados estruturados não concordam com o texto visível

Armazene ambos os valores e defina a precedência para o seu caso de uso. Auditorias de busca podem preferir o cabeçalho JSON-LD; monitoramento de conteúdo pode preferir o H1 visível. Um desalinhamento é dado, não apenas um erro.

Um campo muda de objeto para lista

Normalize na fronteira. Autores e imagens comumente alternam entre um objeto e uma matriz à medida que o CMS de um editor evolui.

Conclusão

Um scraper JSON-LD confiável faz quatro coisas: coleta todos os scripts correspondentes, decodifica sem ocultar blocos malformados, normaliza dicionários, listas e @graph, e valida um pequeno contrato a montante. Esse caminho é mais curto e geralmente mais estável do que reconstruir o mesmo registro a partir de seletores de layout de página. Mantenha o DOM visível como uma verificação cruzada, retenda a proveniência da fonte e introduza a renderização apenas quando o HTML inicial provar que é necessário.

Comece com o plano gratuito da Scrapeless, revise a documentação do desenvolvedor, e verifique preços Scrapeless antes de mover um corpus renderizado para a produção.

FAQ

P: É mais fácil fazer scraping de JSON-LD do que do HTML visível?

Copy
Sim, quando o editor inclui os campos de que você precisa. JSON-LD oferece propriedades e tipos nomeados, enquanto o HTML visível muitas vezes requer seletores e limpeza de texto; você ainda deve comparar campos críticos com a página renderizada.

**Q: Por que devo analisar cada script `application/ld+json`?**

Uma página pode colocar diferentes entidades em blocos separados. Ler apenas o primeiro script pode retornar a organização ou caminhada e perder o artigo ou produto que você queria.

**Q: O que significa `@graph` para extração?**

`@graph` agrupa vários nós JSON-LD dentro de um objeto. Achate o gráfico, depois selecione nós por `@type`, `@id`, URL ou outro identificador estável.

**Q: E se uma propriedade JSON-LD estiver ausente?**

Mantenha propriedades opcionais como `None` e falhe apenas quando um campo exigido pelo seu próprio contrato subsequente estiver ausente. O Schema.org descreve propriedades possíveis; não obriga os editores a preenchê-las todas.

**Q: O JSON-LD pode diferir da página visível?**

Sim. Metadados e conteúdo visível podem ser atualizados em horários diferentes ou otimizados para superfícies diferentes. Armazene ambos os valores quando a diferença for importante e torne a precedência explícita.

**Q: Preciso de um navegador para extrair JSON-LD?**

Não quando o script estiver presente no HTML inicial. Você precisa de renderização apenas quando o JavaScript do lado do cliente insere ou modifica os dados estruturados após a resposta bruta chegar.

**Q: Extrair JSON-LD público é sempre permitido?**

Nenhuma regra geral torna toda coleta legal ou permitida. Revise os termos do site e as <a href="https://datatracker.ietf.org/doc/html/rfc9309" rel="nofollow"><strong>diretivas de robôs</strong></a>, mantenha o volume de solicitações limitado, colete apenas os campos que você precisa e obtenha aconselhamento legal para usos sensíveis ou comerciais.

Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.

Artigos mais populares

Catálogo