Engenharia de dados
Coletores de preços
Robôs em Python que leem a API das lojas virtuais e trazem todos os preços, com o código de barras conferido.
O desafio
Onde tudo começou.
Acompanhar os preços do mercado significa ler dezenas de lojas virtuais, e cada rede publica os dados de um jeito. Um código de barras errado faz a comparação cair no produto errado.
A solução
Da ideia à implementação.
Criei um coletor por rede, apoiado em motores compartilhados para cada plataforma de e-commerce. Todos entregam o mesmo contrato: EAN válido, loja, origem, horário e cada tipo de preço em seu próprio campo.
Como funciona
Por dentro do fluxo.
O coletor consulta a API de catálogo da loja, categoria por categoria, valida o GTIN pelo dígito verificador e separa preço regular, promocional, de atacado, de clube e de cartão. Itens sem EAN válido passam por uma recuperação segura antes de serem descartados; nada é preenchido por aproximação. No Airflow, cada loja tem a sua DAG.
- 1Loja virtual
- 2Coletor da rede
- 3Validação de EAN
- 4Preços por tipo
- 5Lote completo ou nada
- 6Banco de dados
Por dentro do código
Como um coletor funciona, no código.
Trechos simplificados do coletor real. Nomes de rede e endereços foram trocados.
1Cada rede é uma classe de dez linhas.scrapers/rede_exemplo.py
O arquivo de uma rede só diz onde fica a loja. A coleta vem de um motor que já sabe conversar com aquela plataforma de e-commerce, então uma rede nova na mesma plataforma entra sem reescrever nada.
from scrapers.shared.vtex_checkout import VtexCheckoutScraper
class RedeExemploScraper(VtexCheckoutScraper):
MARKET_INFO = {
"slug": "rede_exemplo",
"platform": "VTEX",
"default_domain": "https://www.loja-exemplo.com.br",
}
SETTING_PREFIX = "REDE_EXEMPLO"2O catálogo inteiro, categoria por categoria.scrapers/shared/vtex_checkout.py
O motor pede a árvore de categorias à API da loja e percorre cada uma de 50 em 50 produtos. Se a loja responde com erro, ele espera, tenta de novo e divide a faixa ao meio até passar.
def _iter_category_pages(self, session, category_path, max_pages):
for page in range(max_pages):
# API de catálogo da própria loja: 50 produtos por chamada
payload = self._fetch_catalog_range(
session, category_path, page * 50, page * 50 + 49
)
if not payload:
break
yield payload
if len(payload) < 50: # última página da categoria
break3O preço que vale na região da loja.scrapers/shared/vtex_checkout.py
O preço do catálogo nem sempre é o da loja daquela cidade. Por isso o coletor simula o carrinho com o CEP da loja e em quantidades diferentes: quando a unidade fica mais barata levando 3, 6 ou 12, esse é o preço de atacado.
# O mesmo produto no carrinho, em várias quantidades,
# com o CEP e o vendedor da loja monitorada.
quantities = [1, 3, 6, 12]
snapshots = {
quantity: self._checkout_snapshot(session, records, quantity)
for quantity in quantities
}
prices = self._classify_prices(snapshots[1], snapshots)
# regular, promocional, atacado, clube e cartão4Ou vem tudo, ou não grava.scrapers/shared/vtex_checkout.py
Categorias que falham ganham uma segunda passada, a partir da página em que pararam. Se ainda faltar alguma, a coleta inteira falha no Airflow: preço pela metade engana mais do que preço nenhum.
failed = self._retry_failed_categories(failed, max_pages)
if failed:
# Faltou categoria: a execução falha no Airflow
# e o lote parcial não vai para o banco.
raise RuntimeError("Coleta incompleta")
self.finish() # valida EAN, remove duplicados e entrega o loteO robô lê a loja. O contrato confere.
Pronto. O robô lê cada produto e confere o código de barras.
Em resumo
- 59 DAGs de coleta, uma por loja
- 36 coletores: supermercados, atacados, farmácias, pet shops e outros ramos
- EAN conferido pelo dígito verificador
- Cinco tipos de preço, cada um em seu campo
- Coleta que falha não grava lote pela metade
Minha parte
Criei os coletores e as DAGs: motores por plataforma, regras de EAN e de tipos de preço, novas tentativas e a entrega dos lotes à central.
Resultado e situação
Hoje são 59 DAGs de coleta no ar, uma por loja, entre supermercados, atacados, farmácias e pet shops. Com as de ETL e publicação, são 106 DAGs, todas criadas por mim. Numa execução real de 10/10/2026, uma loja inteira foi lida em 16 min 37 s: 7.746 produtos com EAN válido, em 515 corredores e 1.422 páginas, com pico de 109 MB de memória.
Contexto do projeto
Projeto real, criado por mim para a empresa em que trabalho hoje. O código é privado: os trechos desta página são versões simplificadas, com nomes de redes e endereços trocados. O método serve para qualquer ramo que publique preço na internet.