Crawl4AI: Web Scraping com IA em Python

Aprenda a usar Crawl4AI para web scraping inteligente com LLMs em Python. Extração estruturada, crawling assíncrono e exemplos.

7 min de leitura Equipe python.dev.br

Web scraping tradicional exige que você escreva seletores CSS, XPath ou expressões regulares para cada site. Quando a estrutura da página muda, tudo quebra. O Crawl4AI propõe uma abordagem diferente: usar modelos de linguagem (LLMs) para entender o conteúdo da página e extrair dados de forma inteligente, sem depender da estrutura HTML.

Com mais de 30 mil estrelas no GitHub, o Crawl4AI se tornou a principal biblioteca open-source de web scraping com IA em Python. Neste artigo, vamos ver como instalar, configurar e usar o Crawl4AI para extrair dados estruturados de qualquer página web.

O que é o Crawl4AI?

O Crawl4AI é uma biblioteca Python que combina crawling assíncrono com extração baseada em LLMs. Ele usa um navegador headless (Playwright) para renderizar páginas — incluindo conteúdo JavaScript — e depois processa o HTML com estratégias de extração que podem ser baseadas em CSS tradicional ou em modelos de IA.

As principais funcionalidades incluem:

  • Crawling assíncrono com suporte a múltiplas páginas simultâneas
  • Extração com LLM usando modelos locais ou APIs como OpenAI e Claude
  • Estratégias de extração configuráveis (CSS, cosine similarity, LLM)
  • Conversão automática para Markdown otimizado para LLMs
  • Suporte a JavaScript via Playwright
  • Cache inteligente para evitar requisições desnecessárias
  • Chunking de conteúdo longo para processamento eficiente

Instalação

A instalação é simples com pip ou UV:

# Com pip
pip install crawl4ai
crawl4ai-setup  # Instala o Playwright e navegadores

# Com UV (mais rápido)
uv add crawl4ai
uv run crawl4ai-setup

O comando crawl4ai-setup baixa o Chromium e configura o Playwright automaticamente.

Crawling Básico

O uso mais simples do Crawl4AI é extrair o conteúdo de uma página como Markdown:

import asyncio
from crawl4ai import AsyncWebCrawler


async def main():
    async with AsyncWebCrawler() as crawler:
        resultado = await crawler.arun(url="https://python.org")

        print(resultado.markdown[:500])
        print(f"\nLinks encontrados: {len(resultado.links['internal'])}")
        print(f"Imagens: {len(resultado.media['images'])}")


asyncio.run(main())

O Crawl4AI retorna um objeto CrawlResult com várias representações do conteúdo:

  • resultado.html — HTML original
  • resultado.markdown — conteúdo convertido para Markdown
  • resultado.cleaned_html — HTML limpo sem scripts e estilos
  • resultado.links — links internos e externos
  • resultado.media — imagens, vídeos e áudios encontrados

Extração Estruturada com LLM

O diferencial do Crawl4AI é usar LLMs para extrair dados estruturados. Em vez de escrever seletores CSS, você define um schema Pydantic e deixa o modelo extrair as informações:

import asyncio
from pydantic import BaseModel
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import LLMExtractionStrategy


class Produto(BaseModel):
    nome: str
    preco: float
    descricao: str
    disponivel: bool


async def extrair_produtos(url: str) -> list[Produto]:
    estrategia = LLMExtractionStrategy(
        provider="openai/gpt-4o-mini",
        schema=Produto.model_json_schema(),
        instruction="Extraia todos os produtos da página com nome, preço, descrição e disponibilidade.",
    )

    async with AsyncWebCrawler() as crawler:
        resultado = await crawler.arun(
            url=url,
            extraction_strategy=estrategia,
        )

        return [Produto(**item) for item in resultado.extracted_content]


produtos = asyncio.run(extrair_produtos("https://exemplo.com/produtos"))
for p in produtos:
    print(f"{p.nome}: R$ {p.preco:.2f}")

Esse padrão funciona para qualquer tipo de dado: artigos de blog, ofertas de emprego, reviews, catálogos e mais. O LLM entende o contexto da página e extrai os campos mesmo quando a estrutura HTML varia.

Extração sem LLM: CSS e Cosine Strategy

Nem tudo precisa de IA. O Crawl4AI oferece estratégias de extração que não dependem de LLMs:

CSS Extraction Strategy

Para sites com estrutura conhecida, a extração por CSS é mais rápida e barata:

import asyncio
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy

schema = {
    "name": "Artigos",
    "baseSelector": "article.post",
    "fields": [
        {"name": "titulo", "selector": "h2 a", "type": "text"},
        {"name": "link", "selector": "h2 a", "type": "attribute", "attribute": "href"},
        {"name": "resumo", "selector": ".excerpt", "type": "text"},
        {"name": "data", "selector": "time", "type": "attribute", "attribute": "datetime"},
    ],
}


async def main():
    estrategia = JsonCssExtractionStrategy(schema)

    async with AsyncWebCrawler() as crawler:
        resultado = await crawler.arun(
            url="https://blog.exemplo.com",
            extraction_strategy=estrategia,
        )

        for artigo in resultado.extracted_content:
            print(f"- {artigo['titulo']}")


asyncio.run(main())

Cosine Strategy

A estratégia de similaridade por cosseno agrupa conteúdo semanticamente relacionado sem precisar de seletores:

from crawl4ai.extraction_strategy import CosineStrategy

estrategia = CosineStrategy(
    semantic_filter="vagas de emprego python",
    word_count_threshold=50,
    sim_threshold=0.3,
)

Crawling de Múltiplas Páginas

Para projetos que precisam raspar várias páginas, o Crawl4AI oferece crawling assíncrono eficiente:

import asyncio
from crawl4ai import AsyncWebCrawler


async def crawl_multiplas(urls: list[str]):
    async with AsyncWebCrawler() as crawler:
        resultados = await crawler.arun_many(
            urls=urls,
            max_concurrent=5,  # Máximo de páginas simultâneas
        )

        for resultado in resultados:
            if resultado.success:
                print(f"✓ {resultado.url}: {len(resultado.markdown)} chars")
            else:
                print(f"✗ {resultado.url}: {resultado.error_message}")


urls = [
    "https://python.org",
    "https://pypi.org",
    "https://docs.python.org/3/",
]

asyncio.run(crawl_multiplas(urls))

Configurações Avançadas

Executar JavaScript antes da extração

Páginas com conteúdo dinâmico podem precisar de interações antes da extração:

async with AsyncWebCrawler() as crawler:
    resultado = await crawler.arun(
        url="https://exemplo.com/infinite-scroll",
        js_code=[
            "window.scrollTo(0, document.body.scrollHeight);",
            "await new Promise(r => setTimeout(r, 2000));",
        ],
        wait_for="css:.item:nth-child(20)",  # Esperar 20 itens carregarem
    )

Headers e cookies personalizados

async with AsyncWebCrawler(
    headers={"Accept-Language": "pt-BR,pt;q=0.9"},
    cookies=[{"name": "session", "value": "abc123", "domain": "exemplo.com"}],
) as crawler:
    resultado = await crawler.arun(url="https://exemplo.com")

Cache para desenvolvimento

Durante o desenvolvimento, o cache evita requisições repetidas:

async with AsyncWebCrawler() as crawler:
    # Primeira chamada: faz a requisição
    resultado = await crawler.arun(url="https://exemplo.com", cache_mode="enabled")

    # Segunda chamada: usa o cache
    resultado = await crawler.arun(url="https://exemplo.com", cache_mode="enabled")

Crawl4AI vs BeautifulSoup vs Scrapy

RecursoBeautifulSoupScrapyCrawl4AI
Extração com LLMNãoNãoSim
JavaScript renderingNãoCom pluginSim (Playwright)
Assíncrono nativoNãoSimSim
Conversão para MarkdownNãoNãoSim
Curva de aprendizadoBaixaMédiaBaixa
Ideal paraParsing simplesCrawling em escalaExtração inteligente

Para projetos que precisam apenas de parsing HTML simples, BeautifulSoup com requests continua sendo uma boa escolha. Para scraping em larga escala com pipelines complexos, Scrapy é maduro e robusto. O Crawl4AI brilha quando você precisa extrair dados estruturados de páginas variadas sem escrever seletores específicos para cada uma.

Exemplo Completo: Monitorando Vagas de Python

Vamos juntar tudo em um exemplo prático que monitora vagas de emprego:

import asyncio
import json
from datetime import datetime
from pydantic import BaseModel
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import LLMExtractionStrategy


class Vaga(BaseModel):
    titulo: str
    empresa: str
    localizacao: str
    senioridade: str
    salario: str | None = None
    tecnologias: list[str]


async def monitorar_vagas():
    estrategia = LLMExtractionStrategy(
        provider="openai/gpt-4o-mini",
        schema=Vaga.model_json_schema(),
        instruction=(
            "Extraia todas as vagas de emprego relacionadas a Python. "
            "Identifique título, empresa, localização, nível de senioridade, "
            "salário (se disponível) e tecnologias mencionadas."
        ),
    )

    urls = [
        "https://exemplo.com/vagas/python",
        "https://exemplo.com/vagas/backend",
    ]

    async with AsyncWebCrawler() as crawler:
        resultados = await crawler.arun_many(
            urls=urls,
            extraction_strategy=estrategia,
            max_concurrent=3,
        )

        todas_vagas = []
        for resultado in resultados:
            if resultado.success and resultado.extracted_content:
                vagas = [Vaga(**v) for v in resultado.extracted_content]
                todas_vagas.extend(vagas)

        # Salvar resultados
        with open("vagas.json", "w", encoding="utf-8") as f:
            json.dump(
                [v.model_dump() for v in todas_vagas],
                f,
                ensure_ascii=False,
                indent=2,
            )

        print(f"Encontradas {len(todas_vagas)} vagas em {datetime.now():%Y-%m-%d %H:%M}")
        for vaga in todas_vagas[:5]:
            print(f"  - {vaga.titulo} @ {vaga.empresa} ({vaga.senioridade})")


asyncio.run(monitorar_vagas())

Boas Práticas

Ao usar o Crawl4AI em projetos reais, considere estas práticas:

  1. Respeite o robots.txt: verifique se o site permite scraping antes de começar.
  2. Use rate limiting: configure intervalos entre requisições para não sobrecarregar servidores.
  3. Prefira extração CSS quando possível: é mais rápida e não consome tokens de LLM.
  4. Cache durante desenvolvimento: evite requisições desnecessárias enquanto ajusta o código.
  5. Trate erros: páginas podem falhar, retornar 403 ou ter estrutura inesperada.
  6. Monitore custos de LLM: cada extração com LLM consome tokens. Use modelos menores para tarefas simples.

Se você trabalha com logging e tratamento de erros, esses conhecimentos são essenciais para robustez em projetos de scraping.

Conclusão

O Crawl4AI traz o web scraping para uma nova era. Em vez de batalhar com seletores frágeis que quebram a cada mudança no HTML, você descreve o que quer extrair e deixa o LLM fazer o trabalho pesado de entender a estrutura da página.

Para projetos que precisam extrair dados de sites variados, lidar com conteúdo dinâmico e produzir resultados estruturados, o Crawl4AI é a ferramenta mais produtiva disponível em Python hoje. E para quem já domina scraping tradicional com BeautifulSoup e requests, a migração é natural — os conceitos são os mesmos, só a estratégia de extração muda.

Se você quer explorar mais ferramentas modernas do ecossistema Python, confira nossos artigos sobre Polars, Pydantic e automação com Python.

Para scraping em larga escala com performance máxima, Go com Colly oferece crawling concorrente extremamente eficiente com goroutines — ideal para quando você precisa raspar milhões de páginas e o gargalo é a velocidade de rede, não a extração de dados.

E

Equipe python.dev.br

Contribuidor do Python Brasil — Aprenda Python em Português