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.

LEIA TAMBÉM · TutorialSeu próprio bot no Telegram, com Claude respondendo do outro ladoUm bot de Telegram é um programa que recebe sua mensagem no app e responde sozinho. Aqui ele usa uma IA (Claude) pra gerar a resposta. É o primeiro projeto de sistema — não só de prompt — de quem está começando.

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.

LEIA TAMBÉM · ExplicaçãoO que separa um protótipo de IA de um sistema em produção são três peças baratasMostrar IA funcionando uma vez é fácil. O difícil é deixar rodando todo dia sem ninguém olhando. Três peças simples — Docker para empacotar, healthcheck para vigiar, agendador para disparar — fazem quase todo o trabalho.

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.