Instalar httpx e selectolax, envie uma solicitação GET com um cabeçalho User-Agent realista e selecione o preço com um único seletor CSS. Esse é um scraper funcional com cerca de 15 linhas de código. Adicione proxies e tentativas de repetição assim que começar a ser bloqueado, o que, em sites de varejo reais, ocorre rapidamente.
Como extrair preços com Python: um guia passo a passo (2026)
A extração de preços com Python consiste em escrever um script que recupere a página de um produto, identifique o preço no código HTML ou na saída renderizada e o salve como dados estruturados, que você possa comparar ou acompanhar ao longo do tempo. A versão mais simples consiste em uma solicitação HTTP e um analisador de HTML. As partes mais complexas são as defesas contra bots e os preços renderizados por JavaScript, e é a isso que se dedica a maior parte deste guia.
Este é o guia técnico prático que faz parte do nosso guia mais abrangente sobre monitoramento de preços da concorrência. Se desejar uma visão geral da estratégia e das ferramentas, comece por aí. Se desejar um código executável, continue lendo.
Pontos principais
- Um scraper de preços minimalista é um cliente HTTP (
solicitaçõesouhttpx) além de um analisador sintático (selectolaxou BeautifulSoup) que tem como alvo um seletor CSS. - A parte delicada não é a análise, e sim o acesso: limites de taxa, filtragem de user-agent, geo-cloaking e bloqueios diretos de IP.
- A alternância de proxies residenciais é importante para páginas de preços, pois o preço exibido depende do país e da reputação do endereço IP da solicitação.
- Os preços renderizados em JavaScript exigem um navegador sem interface gráfica, uma chamada de API obtida por engenharia reversa ou um serviço de conversão para Markdown.
- Armazene cada observação como uma linha com um carimbo de data e hora e a moeda, para que você possa acompanhar as alterações, e não apenas instantâneos. Respeite os Termos de Serviço e o arquivo robots.txt de cada site.
O que é a extração de preços?
A coleta de preços consiste na extração automatizada de preços de produtos (e, geralmente, de campos relacionados, como título, moeda, disponibilidade e SKU) de páginas da web. Os varejistas fazem isso para monitorar os concorrentes. As marcas fazem isso para garantir o cumprimento dos preços mínimos anunciados. Os agregadores fazem isso para alimentar comparação de preços de web scraping sites.
Os preços são um alvo de alto valor e ativamente protegido. O Relatório Imperva Bad Bot 2025 (uma empresa da Thales) constatou que o tráfego automatizado ultrapassou a atividade humana pela primeira vez em uma década, representando 51% de todo o tráfego da web em 2024, com os bots maliciosos representando 37%. O comércio eletrônico está entre os setores mais visados; por isso, as páginas de preços contam com as mesmas defesas que bloqueiam os bots de “credential stuffing” e de acúmulo de estoque. Seu scraper precisa se passar por um visitante comum.
Etapa 1: Um scraper de preços em Python com código mínimo
Comece com a coisa mais simples que possa funcionar: uma única solicitação GET e um analisador. Nós usamos httpx (um moderno solicitações(cliente compatível) e selectolax (um analisador HTML rápido desenvolvido com base no lexbor).
pip install httpx selectolax
import httpx
from selectolax.parser import HTMLParser
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9",
}
def scrape_price(url: str, price_selector: str) -> str | None:
resp = httpx.get(url, headers=HEADERS, timeout=20.0, follow_redirects=True)
resp.raise_for_status()
tree = HTMLParser(resp.text)
node = tree.css_first(price_selector)
return node.text(strip=True) if node else None
if __name__ == "__main__":
price = scrape_price("https://example.com/product/123", "span.price")
print(price)
A única parte específica do local é seletor_de_preço. Abra a página do produto no seu navegador, inspecione o elemento de preço e copie um seletor estável (uma classe ou dados- atributo, e não um profundo div > div > div cadeia que se rompe na próxima implantação).
Os preços são retornados como strings desorganizadas, como "$1.299,00" ou "1.299,00 €". Normalize antes de armazenar:
import re
from decimal import Decimal
def parse_amount(raw: str) -> Decimal | None:
if not raw:
return None
# Remova tudo, exceto dígitos e separadores
cleaned = re.sub(r"[^\d.,]", "", raw)
# Heurística: se a vírgula for o separador decimal (por exemplo, “1.299,00”)
if cleaned.count(",") == 1 and cleaned.rfind(",") > cleaned.rfind("."):
cleaned = cleaned.replace(".", "").replace(",", ".")
else:
cleaned = cleaned.replace(",", "")
try:
return Decimal(cleaned)
except Exception:
return None
Mantenha sempre o símbolo da moeda original separado. Não presuma que se trata de USD.
Etapa 2: Lidar com as defesas contra bots
O exemplo acima funciona em um site de teste, mas apresenta falhas na maioria dos sites de varejo reais. Veja a seguir quais problemas você poderá encontrar e como lidar com cada um deles.
Limites de taxa e novas tentativas
Fazer um ataque de martelada a um site é a maneira mais rápida de ser bloqueado e a atitude mais grosseira que você pode ter. Adicione atrasos, jitter e recuo exponencial às respostas que significam “diminua o ritmo” (429) ou “temporariamente indisponível” (503).
import random
import time
import httpx
def get_with_backoff(client: httpx.Client, url: str, max_tries: int = 4):
for attempt in range(max_tries):
resp = client.get(url, timeout=20.0)
if resp.status_code not in (429, 503):
return resp
wait = (2 ** attempt) + random.uniform(0, 1.0)
time.sleep(wait)
resp.raise_for_status()
return resp
Agentes de usuário e cabeçalhos
Uma solicitação sem cabeçalhos grita “script”. Envie um User-Agent realista e atual de um navegador, bem como os cabeçalhos que um navegador de verdade envia (Aceitar, Accept-Language, Accept-Encoding). Alternar entre um pequeno conjunto de strings de UA reais e recentes ajuda, mas uma UA verossímil associada a um endereço IP sinalizado ainda é bloqueada.
Bloqueios de IP, geo-cloaking e por que os proxies residenciais são importantes
Essa é a parte que a maioria dos tutoriais ignora. Duas solicitações para a mesma URL de produto podem retornar preços diferentes, moedas diferentes ou uma página de bloqueio, dependendo inteiramente do endereço IP do solicitante.
- Endereços IP do centro de dados (os intervalos de preços mais comuns entre os servidores em nuvem) são fáceis de identificar e costumam ter sua oferta limitada ou serem totalmente bloqueados nas páginas de preços.
- Ocultação geográfica Isso significa que um site exibe o preço em dólares para um visitante dos EUA e o preço em euros para um visitante da Alemanha. Se você coletar dados a partir de uma única região de datacenter, verá sempre apenas os preços de um único mercado e, às vezes, uma mensagem do tipo “não disponível na sua região”.
Os proxies residenciais rotativos encaminham as solicitações por meio de conexões reais de consumidores; assim, eles apresentam a reputação de IP de um visitante comum e permitem que você escolha o país (e, às vezes, a cidade) de onde a solicitação parece ter origem. Para a coleta de preços, isso significa que você visualiza o preço real localizado para cada mercado e evita os bloqueios de IPs de data centers que restringem o acesso a essas páginas. A Massive opera uma rede residencial em mais de 195 países (HTTP/HTTPS/SOCKS5) com segmentação geográfica por país/cidade e sessões rotativas ou fixas.
Para configurar um proxy no cliente, basta alterar uma linha:
import os
import httpx
# Proxy residencial Massive. As credenciais devem ser inseridas na URL do proxy no formato usuário:senha.
PROXY = (
f"https://{os.environ['MASSIVE_PROXY_USERNAME']}:"
f"{os.environ['MASSIVE_API_KEY']}@network.joinmassive.com:65535"
)
with httpx.Client(proxy=PROXY, headers=HEADERS, timeout=20.0) as client:
resp = client.get("https://example.com/product/123")
print(resp.status_code)
Utilize um giratório sessão, quando se deseja um endereço IP novo a cada solicitação (ideal para distribuir a carga) e um em destaque sessão, quando um fluxo precisa do mesmo endereço IP em várias solicitações (por exemplo, definir um cookie de região e, em seguida, carregar o preço). Para um passo a passo específico para varejistas, consulte extrair preços da Amazon sem ser bloqueado.
Etapa 3: Tratar preços renderizados por JavaScript
Muitas lojas online enviam uma estrutura HTML praticamente vazia e geram o preço no lado do cliente com JavaScript. Se resp.text caso não contenha o preço, o analisador da Etapa 1 retorna Nenhum por melhor que seja o seu seletor. Você tem três opções.
Opção A: Identifique a API subjacente. Abra o DevTools, acesse a guia “Rede”, filtre por “XHR/Fetch” e atualize a página. O preço quase sempre é fornecido em uma resposta JSON. Acessar esse endpoint diretamente é mais rápido e mais estável do que renderizar a página, desde que a API em si não esteja restrita.
Opção B: Executar um navegador sem interface gráfica. O Playwright renderiza a página no Chromium de verdade, de modo que o preço já está presente no DOM no momento em que o senhor o lê.
from playwright.sync_api import sync_playwright
def scrape_rendered_price(url: str, selector: str) -> str | None:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
página = navegador.new_page()
página.goto(url, wait_until="networkidle")
nó = página.query_selector(seletor)
valor = nó.inner_text() se nó, caso contrário None
navegador.close()
retorne valor
Os navegadores sem interface gráfica são pesados: consomem muito mais CPU e memória do que uma solicitação HTTP e são mais lentos para escalar.
Opção C: Converter para Markdown (da maneira mais fácil). Este é o caminho de menor resistência. Uma Web Render API executa a renderização sem interface gráfica na infraestrutura de terceiros: navegadores reais executam o JavaScript da página, com o resultado final convertido em Markdown limpo e devolvido ao seu próprio código. A Web Render API da Massive possui um endpoint de navegação que faz exatamente isso. O senhor evita tanto a sobrecarga do navegador sem interface gráfica quanto a maior parte do trabalho de análise de HTML, pois está procurando texto legível para identificar a linha de preço, em vez de percorrer um DOM instável; além disso, o Markdown é enviado diretamente para um cliente de IA ou prompt de LLM, caso deseje extrair o preço dessa forma.
import os
import re
import httpx
# Web Render API da Massive, endpoint de navegação: renderiza a página na
# rede da Massive e retorna o Markdown limpo.
def render_markdown(url: str, country: str = "US") -> str:
resp = httpx.get(
"https://render.joinmassive.com/browser",
params={"url": url, "format": "markdown", "country": country},
headers={"Authorization": f"Bearer {os.environ['MASSIVE_API_TOKEN']}"},
timeout=60.0, # a renderização é mais pesada do que uma chamada GET simples; reserve tempo suficiente
)
resp.raise_for_status()
return resp.text
# O Markdown é muito mais fácil de analisar do que HTML bruto. Passe o país para visualizar o
# preço da forma como um comprador local o vê (preço em dólares americanos, preço em euros e assim por diante).
markdown = render_markdown("https://example.com/product/123")
match = re.search(r"\$[\d,]+\.\d{2}", markdown)
price = match.group(0) if match else None
Etapa 4: Estruturar e armazenar os dados
Um preço só é útil quando considerado como uma série temporal. Armazene cada observação em uma linha separada, acompanhada de um carimbo de data e hora, para que você possa detectar quedas, acompanhar os concorrentes e criar um sistema de monitoramento de preços por cima.
import sqlite3
from datetime import datetime, timezone
from decimal import Decimal
def init_db(path: str = "prices.db") -> sqlite3.Connection:
conn = sqlite3.connect(path)
conn.execute(
"""
CREATE TABLE IF NOT EXISTS observations (
id INTEGER PRIMARY KEY,
sku TEXT NOT NULL,
url TEXT NOT NULL,
amount TEXT NOT NULL, -- armazene Decimal como texto, nunca como float
currency TEXT NOT NULL,
country TEXT, -- de qual mercado esse preço provém
scraped_at TEXT NOT NULL
)
"""
)
return conn
def record(conn, sku: str, url: str, amount: Decimal, currency: str, country: str):
conn.execute(
"INSERT INTO observations (sku, url, amount, currency, country, scraped_at) "
"VALUES (?, ?, ?, ?, ?, ?)",
(sku, url, str(amount), currency, country,
datetime.now(timezone.utc).isoformat()),
)
conn.commit()
Duas regras que vão lhe poupar trabalho mais tarde: armazene o valor como texto ou como um número inteiro correspondente à contagem de unidades menores (nunca como um número real binário, que perde os centavos) e sempre registre o moeda e país. Um “$50” e um “50 EUR” na mesma coluna, sem indicação de moeda, constituem um erro oculto nos dados. Para um CSV ou um pipeline de warehouse, o esquema é o mesmo; a linha com data e hora e com a tag de mercado é a unidade que importa. Se preferir comprar em vez de desenvolver, compare ferramentas de monitoramento de preços da concorrência.
Nota sobre a legalidade e os Termos de Serviço
Isso não constitui aconselhamento jurídico, mas vale a pena conhecer o panorama prático. A coleta de dados visíveis ao público tem, repetidamente, resistido a contestações nos Estados Unidos com base na Lei de Fraude e Abuso Informático (Computer Fraud and Abuse Act): no caso de longa duração hiQ Labs contra LinkedIn No âmbito desse litígio, o Nono Circuito reafirmou, em 2022, que o acesso a dados de sites disponíveis publicamente provavelmente não constitui acesso “sem autorização” nos termos da CFAA. Essa decisão referia-se especificamente à CFAA, não constituindo uma autorização geral. Outras teorias (violação dos Termos de Serviço de um site, direitos autorais, invasão de propriedade móvel e legislação de privacidade) ainda podem ser aplicáveis.
Medidas de segurança práticas: leia e respeite os Termos de Serviço de cada site e robots.txt, extraia apenas dados públicos de preços (nunca nada que esteja protegido por login e que você tenha concordado em não extrair), aplique limites de taxa para não prejudicar o funcionamento do site e evite dados pessoais. Em caso de dúvida, consulte o departamento jurídico.
Construa-o em uma rede que reconheça o preço real
O código é a parte fácil. O acesso consistente a preços corretos e localizados é a parte difícil, e tudo se resume à origem aparente de suas solicitações. A rede residencial da Massive (mais de 195 países, segmentação geográfica por país/cidade, sessões rotativas ou fixas) e a Web Render API (páginas renderizadas como Markdown limpo) resolvem ambos os problemas: a reputação do IP e a segmentação geográfica, que permitem que o senhor contorne bloqueios, e a renderização, que fornece preços carregados via JavaScript sem que o senhor precise executar navegadores por conta própria. Conheça a Web Render API da Massive e os proxies residenciais.
Fontes
- Imperva (uma empresa da Thales), “Relatório Imperva sobre bots maliciosos de 2025: Como a IA está intensificando a ameaça dos bots”, https://www.imperva.com/blog/2025-imperva-bad-bot-report-how-ai-is-supercharging-the-bot-threat/ (consultado em 15 de junho de 2026)
- hiQ Labs, Inc. v. LinkedIn Corp., 31 F.4th 1180 (9º Cir. 2022) (em novo julgamento, reafirmando que a extração de dados de sites disponíveis publicamente provavelmente não constitui acesso “sem autorização” nos termos da CFAA).
- Jenner & Block LLP, “Extração de dados: no caso hiQ v. LinkedIn, o Nono Circuito reafirma a interpretação restrita da CFAA”, https://www.jenner.com/en/news-insights/publications/client-alert-data-scraping-in-hiq-v-linkedin-the-ninth-circuit-reaffirms-narrow-interpretation-of-cfaa (consultado em 15 de junho de 2026)
Perguntas frequentes
Duas causas comuns. Primeiro, o preço pode ser gerado por JavaScript, portanto não consta no HTML bruto que seu cliente HTTP recebe; você precisará de um navegador sem interface gráfica ou de um serviço de conversão para Markdown. Segundo, o site pode estar utilizando geo-cloaking, exibindo preços diferentes por país, ou bloqueando seu endereço IP. A extração de dados por meio de um IP residencial no próprio país revela o preço localizado correto e evita bloqueios de IPs de data centers.
Para um número reduzido de páginas, não. Para um volume significativo, sim. As páginas de preços são ativamente protegidas, e um único IP de data center que faça solicitações repetidas é rapidamente limitado ou bloqueado. A rotação de proxies residenciais distribui as solicitações por IPs reais de consumidores e permite que você segmente países específicos, o que é necessário quando os preços variam de acordo com o mercado.
Três opções, por ordem de preferência: identificar a API JSON que a página acessa (verifique a guia “Rede” das Ferramentas do Desenvolvedor), utilizar um navegador headless como o Playwright ou usar uma Web Render API que retorne a página totalmente renderizada (a saída em Markdown é a mais fácil de analisar). Simples solicitações/httpx por si só não pode executar JavaScript.
A coleta de dados de preços disponíveis publicamente resistiu a contestações com base na CFAA dos EUA (notadamente hiQ contra LinkedIn), mas isso não abrange os Termos de Serviço, direitos autorais ou reclamações relacionadas à privacidade, e as leis variam de acordo com a jurisdição. Limite-se a dados públicos, respeite robots.txt e os Termos de Serviço, aplique limites de taxa de maneira educada e consulte um advogado para qualquer questão comercial ou de grande porte.
