O concorrente baixou o preço numa terça-feira; você só sabe na sexta, quando um cliente comenta que viu mais barato — ou nem sabe, e a venda não sai. Conferir preços na mão funciona com dois ou três produtos; no quinto, você já pula um dia.
Automatizar vale o esforço, ou basta olhar de vez em quando? Ver o preço é a parte fácil — o que importa é saber a tempo de reagir, sem ruído quando nada mudou. Já montei coletores assim mais de uma vez: o que estraga o projeto quase nunca é o scraper, é o alerta que dispara todo dia até alguém silenciar a conversa. O roteiro abaixo grava histórico, calcula a variação e só chama você acima de um limiar — ao custo de uma lista de seletores CSS que você mantém quando o concorrente troca o layout.
O que você precisa
- Python 3.11 ou mais recente
- Um editor de código — VS Code resolve
- Conta no Supabase; a camada gratuita cobre este projeto
- Um bot no Telegram, criado pelo BotFather em poucos minutos
- Lista de concorrentes e produtos que você já acompanha
- Uma tarde para a primeira versão rodar
- Opcional: VPS barato ou GitHub Actions agendado, para não depender do seu computador ligado
Passo 1: mapeie os concorrentes e o seletor de cada preço
Comece pela lista, não pelo código: um JSON com concorrente, produto, URL e seletor CSS do preço. Incluir um concorrente novo vira editar um arquivo de texto.
[
{
"concorrente": "Loja A",
"produto": "Cadeira ergonômica",
"url": "https://loja-a.exemplo/cadeira",
"seletor": ".price-value"
},
{
"concorrente": "Loja B",
"produto": "Cadeira ergonômica",
"url": "https://loja-b.exemplo/cadeira",
"seletor": "span.amount"
}
]
Para achar o seletor: Chrome, botão direito no preço, "Inspecionar" — pegue a classe mais estável, algo como .price-value ou span.amount. HTML confuso? Cole o trecho numa IA e peça o seletor.
Ao fim do passo, o scraper lê tudo desse arquivo — nenhuma URL fica escrita no código.
Passo 2: tente o HTML puro antes de subir um navegador
Playwright substitui o Selenium com instalação em um comando e espera automática embutida — sem sleep manual. Mas abrir um navegador inteiro para ler um número sai caro numa página estática: tente primeiro requests com BeautifulSoup; se o preço não estiver no HTML bruto, é JavaScript, e aí o Playwright entra só para aquela URL.
pip install requests beautifulsoup4 playwright supabase
playwright install chromium
import requests
from bs4 import BeautifulSoup
from playwright.sync_api import sync_playwright
def fetch_price_static(url: str, selector: str) -> str | None:
resp = requests.get(url, headers={"User-Agent": "Mozilla/5.0"}, timeout=10)
soup = BeautifulSoup(resp.text, "html.parser")
el = soup.select_one(selector)
return el.get_text(strip=True) if el else None
def fetch_price_rendered(url: str, selector: str) -> str | None:
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto(url, timeout=15000)
el = page.query_selector(selector)
text = el.text_content() if el else None
browser.close()
return text
Peça à IA o resto: erro, retry, delay aleatório, user-agent variado. Testado em duas ou três URLs, o script deve imprimir o preço de cada uma.
Passo 3: grave cada leitura em vez de sobrescrever a anterior
Sem histórico não existe variação, só o preço de agora — que você já veria abrindo a página. Crie no Supabase a tabela precos: id, concorrente, produto, preco, url, coletado_em. Cada execução insere linhas novas; nenhuma atualiza uma antiga.
import os
from supabase import create_client
supabase = create_client(os.environ["SUPABASE_URL"], os.environ["SUPABASE_KEY"])
def salvar_preco(concorrente, produto, preco, url):
supabase.table("precos").insert({
"concorrente": concorrente,
"produto": produto,
"preco": preco,
"url": url,
}).execute()
Depois de rodar, a tabela precos no Supabase mostra uma linha por produto por execução, com a hora da coleta — é esse acúmulo que os dois passos seguintes consomem.
Passo 4: transforme preço em variação antes de pensar em alerta
Um preço isolado não diz nada. Três comparações dizem: contra a leitura anterior (o movimento), contra o seu preço (a posição) e contra a média de 30 dias (oscilação normal ou fora do padrão). Dois limiares governam o alerta: variação maior que 5% contra a leitura anterior, e preço mais de 10% abaixo da média de 30 dias — números de partida, ajustáveis depois de duas semanas de histórico.
def analisar(concorrente: str, produto: str, preco_atual: float, meu_preco: float) -> dict:
# consulta o histórico em `precos` e devolve:
# {"variacao_pct": ..., "diferenca_vs_meu_preco": ..., "abaixo_da_media": ...}
...
Peça à IA as consultas ao Supabase que preenchem o dicionário. O passo termina quando a função devolve os três campos.
Passo 5: dispare o alerta no Telegram apenas acima do limiar
Fale com @BotFather no Telegram, mande /newbot e guarde o token. Falta o chat_id: mande uma mensagem ao bot e consulte getUpdates.
import requests
def alertar_telegram(token: str, chat_id: str, texto: str):
url = f"https://api.telegram.org/bot{token}/sendMessage"
requests.post(url, data={"chat_id": chat_id, "text": texto})
A linha entre alerta útil e ruído é uma condição só: chame alertar_telegram apenas quando o Passo 4 passar do limiar. A mensagem traz produto, concorrente, preço anterior, preço novo e a variação.
Passo 6: agende a execução e rode o circuito ponta a ponta
De seis em seis horas cobre a maioria dos mercados. No Linux, uma linha no cron:
0 */6 * * * cd /caminho/projeto && python scraper.py
No Windows, o Agendador de Tarefas faz o mesmo; sem servidor próprio, o GitHub Actions roda com schedule: cron. Depois de uma execução: linha nova em precos e, se mudou o bastante, mensagem no Telegram.
O que pode dar errado
O site devolve 403 ou 429 e o scraper volta vazio. Comece por user-agent realista e delay aleatório; se continuar, é frequência: de seis em seis horas passa despercebido, de minuto em minuto não.
O requests traz a página, mas o seletor não encontra nada, e no navegador o preço está lá — é JavaScript montando depois do carregamento. Marque essa URL para o Playwright.
O site muda o layout e o seletor para de casar — erro silencioso: sem preço novo, o sistema conclui que está estável. Separe "URL parou de devolver preço" de "preço não mudou"; confundir os dois custa a venda que você queria proteger.
Próximos passos
Três extensões cabem aqui: a primeira já veio de graça no Passo 1 (incluir concorrente é editar o JSON); a segunda fecha o circuito de decisão, ajustando seu preço quando o concorrente cruza um limite, com teto para não corroer a margem; a terceira é um painel simples sobre precos, para ler a curva de 30 dias em vez de só o alerta pontual.
Um preço coletado é uma linha numa tabela. Vira vantagem quando alguém lê o alerta na terça de manhã e muda a etiqueta antes do almoço.
