Como fazer scraping de qualquer site com Python em 2026
Um fluxo com /scrape: busca estática por padrão, escalando para renderização JS e proxies premium só quando necessário, depois parsing com BeautifulSoup.

A maioria dos guias de scraping é escrita para um alvo específico: Google, Amazon, um marketplace específico. Este é para o outro caso, uma URL que você não controla e nunca fez scraping antes, onde você precisa do HTML de forma confiável e depois tem que descobrir o parsing por conta própria.
A requisição básica
Um endpoint, um parâmetro obrigatório:
import requests
API_KEY = "faf_live_..." # sua chave FastAF
resp = requests.get(
"https://api.fastaf.co/v1/scrape",
params={"url": "https://example.com/product/123"},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=60,
)
resp.raise_for_status()
html = resp.text
O corpo da resposta é a própria página, com o seu content type (HTML por padrão, markdown com formats=markdown), não um wrapper JSON.
Por padrão isso é uma busca estática, sem navegador, sem renderização, roteada por infraestrutura construída para não ser bloqueada fazendo isso. Também é o nível mais barato: 1 crédito.
Quando você precisa de mais que o padrão?
Escale em ordem, só até onde o alvo realmente exigir:
- Estático, 1 crédito. Comece sempre por aqui. Funciona para páginas renderizadas no servidor, a maioria dos blogs, sites de documentação, e qualquer página onde o conteúdo que você quer está na resposta HTML inicial.
dynamic=true, 5 créditos. A página constrói o conteúdo com JavaScript depois do carregamento, comum em sites feitos com React/Vue/Svelte e listagens com scroll infinito. Nosso texto sobre renderização JS cobre exatamente como identificar em qual caso você está antes de gastar os créditos extras.premium=true, 10 créditos. A busca estática está sendo bloqueada ou recebendo uma página degradada, não por falta de JavaScript, mas por defesas baseadas em IP. Premium roteia a requisição por infraestrutura de proxy de qualidade superior.- Os dois juntos, 25 créditos. O alvo renderiza no lado do cliente e se defende agressivamente contra IPs automatizados. Reserve esse nível para sites em que você já confirmou que precisa dos dois, não como uma configuração padrão “por via das dúvidas”.
def scrape(url, dynamic=False, premium=False, wait=None):
params = {"url": url}
if dynamic:
params["dynamic"] = "true"
if premium:
params["premium"] = "true"
if wait:
params["wait"] = wait
resp = requests.get(
"https://api.fastaf.co/v1/scrape",
params=params,
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=90,
)
resp.raise_for_status()
return resp.text
wait (em milissegundos) importa mais em requisições com dynamic=true para páginas que disparam uma chamada de API depois do carregamento e renderizam o resultado um instante depois; sem ele você pode receber o estado de carregamento em vez do conteúdo.
Extraindo seus dados
Essa é a parte que continua sendo sua, não importa qual nível você use. /scrape te dá o mesmo HTML que um navegador renderizaria para essa URL, de forma confiável e sem ser bloqueado. Ele não sabe como é um “preço” ou um “título de produto” no seu alvo específico, porque cada site marca isso de um jeito diferente. Esse é um trabalho diferente de /serp, /maps ou /amazon, onde já conhecemos a estrutura da página e entregamos campos já parseados; aqui, a busca está resolvida, o parsing é com você:
from bs4 import BeautifulSoup
html = scrape("https://example.com/product/123")
soup = BeautifulSoup(html, "html.parser")
title = soup.select_one("h1.product-title")
price = soup.select_one("span.price")
print(title.get_text(strip=True) if title else None)
print(price.get_text(strip=True) if price else None)
Escreva seus seletores contra o site alvo da mesma forma que você faria contra um HTML que você mesmo buscou. Se esse site for redesenhado, seus seletores quebram, como em qualquer scraper; o que você removeu do problema foi ser bloqueado e precisar de uma fazenda de navegadores só para buscar a página.
Erros comuns
Usar dynamic=true como padrão para tudo. É um custo 5x maior que o estático, e a maioria das páginas não precisa disso. Verifique primeiro com o método do post sobre renderização JS em vez de pagar o extra por padrão.
Não definir wait em páginas dinâmicas de carregamento lento. Se o conteúdo carrega via uma chamada de API atrasada, o wait padrão pode capturar a página antes dela estar pronta. Se os campos estão voltando vazios de forma consistente em dynamic=true, essa é a primeira coisa a verificar.
Tratar qualquer resposta diferente de 200 como um scrape com falha. Um alvo que responde 404 ou 410 volta como HTTP 422 target_unavailable e não custa nada. Um login wall ou uma página de erro “suave” volta como um 200 normal com essa página no body, então verifique html para ver o que foi realmente retornado.
Fazer parsing antes de checar se html não está vazio. Uma resposta bloqueada ou vazia jogada direto no BeautifulSoup falha dentro do parser em vez de no ponto onde o problema real fica visível. Verifique a resposta primeiro.
Fixar seletores sem alternativa. Um único .select_one() que retorna None em um redesign deveria ser um sinal que você verifica, não uma exceção que derruba o lote inteiro.
Resumindo
Comece estático, a 1 crédito. Escale para dynamic=true só quando confirmar que o conteúdo é renderizado por JavaScript, para premium=true só quando estiver sendo bloqueado no nível de IP, e para os dois juntos só quando um alvo realmente precisar disso. A busca é a parte que a FastAF resolve; fazer o parsing do resultado com BeautifulSoup, seletores e tratamento de erros para o seu alvo específico ainda é a parte que você escreve.