*1.A armadilha do Overengineering *
Quando comecei a desenhar o Gado-Scraper, eu tinha um problema e uma restrição clara: eu precisava monitorar cotações pecuárias diariamente, mas, como estudante, meu orçamento para infraestrutura em nuvem era zero. É muito fácil, hoje em dia, puxar o cartão de crédito e provisionar um banco de dados relacional e uma função serverless na AWS para rodar um script de 1 minuto por dia. Mas eu não podia me dar a esse luxo. Essa restrição financeira me forçou a pensar fora da caixa e evitar a armadilha clássica do overengineering. Foi assim que descobri como transformar o próprio repositório no GitHub em um banco de dados temporal automatizado, com custo absolutamente zero.
Neste artigo, vou te mostrar exatamente como construí uma pipeline ETL de dados que roda todos os dias sem servidor, sem um banco de dados tradicional e com custo exatamente zero. Você vai ver na prática como transformei o próprio repositório no GitHub e o GitHub Actions em um banco de dados temporal automatizado.
2. A Mudança de Paradigma: "Git-as-a-Database"
Mas o que é Git-as-a-Database? Usar o repositório do GitHub como um banco de dados é uma premissa diferente do que o Git foi originalmente pensado, porém se encaixa perfeitamente com o rastreio de mudanças do sistema. Quando paramos para pensar, o Git foi criado com um propósito muito bem definido: rastrear o histórico de alterações em arquivos baseados em texto de forma imutável e cronológica. Bancos de dados tradicionais são incríveis, mas para salvar apenas a variação de uma ou duas cotações uma vez ao dia, eles são um exagero técnico.
Para eliminar a necessidade de um servidor 24h hospedado na nuvem, decidi usar o Github Actions no meu robô de captura. Configurei uma pipeline automatizada que, todo dia em horário de mercado executa meus scripts de web scraping escritos em Python.
O pulo do gato arquitetural acontece logo após a extração. Assim que o script coleta os valores da arroba, ele sobrescreve dois arquivos simples na raiz do projeto: cotacoes_boi_hoje.json e cotacoes_novilha_hoje.json. É aqui que a mágica acontece: em vez de abrir uma conexão pesada com a AWS e fazer um comando INSERT em um banco de dados na nuvem, a pipeline simplesmente faz um git push. O próprio repositório passa a funcionar como o banco de dados histórico. Todo o histórico de preços da pecuária fica perfeitamente preservado no git log, onde cada commit diário atua como um snapshot exato daquele dia, pronto para ser consumido por análises de séries temporais
3. Dissecando a Pipeline YAML
Todo o "cérebro" do robô de captura reside num único ficheiro de configuração YAML. É ele que orquestra toda a nossa infraestrutura efémera.
Logo no início do ficheiro, defini três gatilhos (on): o schedule com uma cron expression (0 9 * * *) configurada para rodar automaticamente todos os dias às 9h UTC (06h da manhã no horário de Brasília), garantindo a captura das cotações no início do dia antes da abertura dos mercados locais; o push para garantir que os dados atualizam caso eu faça alguma alteração no código; e o workflow_dispatch, que me permite disparar o robô manualmente com um botão caso a extração falhe por alguma instabilidade externa.
No entanto, havia um obstáculo técnico de segurança. Por padrão, os runners (servidores temporários) do GitHub Actions possuem permissão apenas de leitura no repositório. Como o nosso objetivo era que o script em Python sobrescrevesse os ficheiros JSON e fizesse o commit dos novos dados, eu precisava de dar autorização de escrita à máquina, tudo isso sem expor as minhas senhas ou criar tokens complexos.
A solução elegante foi ajustar as permissões do job usando a flag permissions: contents: write. Veja como ficou a estrutura real da pipeline:
YAML
name: Atualização Diária (Boi e Novilha)
on:
push: # Roda ao salvar
branches: [ main ]
schedule:
- cron: '0 9 * * *'
workflow_dispatch: # Permite disparo manual
jobs:
raspagem-completa:
runs-on: ubuntu-latest
# PERMISSÃO DE ESCRITA É OBRIGATÓRIA PARA SALVAR O JSON
permissions:
contents: write
steps:
- name: 1. Baixar código do repositório
uses: actions/checkout@v3
- name: 2. Instalar Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: 3. Instalar bibliotecas
run: pip install -r requirements.txt
- name: 4. Rodar Scraper do Boi
run: python scraper_boi.py
- name: 5. Rodar Scraper da Novilha
run: python scraper_novilha.py
- name: 6. Salvar arquivos JSON no GitHub
run: |
git config --global user.name 'Bot do Scraper'
git config --global user.email 'actions@github.com'
# Adiciona os dois arquivos gerados
git add cotacoes_boi_hoje.json cotacoes_novilha_hoje.json
# Só commita se tiver mudança
git diff --quiet && git diff --staged --quiet || (git commit -m "Dados atualizados: $(date +'%Y-%m-%d')" && git push)
Ao definir contents: write, a pipeline ganha a autoridade necessária para fazer o auto-commit direto na branch principal assim que o Python finaliza a extração. Nenhuma configuração adicional, nenhum banco de dados provisionado e nenhum servidor para gerir.
4. Segurança de Execução: Prevenindo falhas na Pipeline
Se prestou atenção no Passo 6 do código acima, deve ter notado um comando Bash um pouco longo na hora de fazer o commit. Existe um motivo crítico para ele estar ali.
Um problema clássico de automações de CI/CD que fazem auto-commit é o pipeline crash (falha no job) quando o Git tenta fazer um commit, mas não existem ficheiros modificados para adicionar à staging area. Para garantir uma execução contínua, segura e resiliente, adicionei a seguinte trava lógica:
Bash
git diff --quiet && git diff --staged --quiet || (git commit -m "Dados atualizados: $(date +'%Y-%m-%d')" && git push)
O comando git diff --quiet atua como um inspetor de segurança do repositório. Como o meu script Python injeta automaticamente a data do dia na chave "data_coleta" do JSON, o repositório garante um snapshot histórico diário, gerando um registo contínuo da série temporal. No entanto, se eu precisar de disparar a pipeline manualmente (workflow_dispatch) mais de uma vez no mesmo dia para fins de debugging, a verificação do diff retorna silêncio (já que a data e os preços daquele dia específico já foram registados na primeira execução).
Quando isso acontece, o fluxo é encerrado de forma limpa, sem tentar forçar um commit vazio que faria a GitHub Action falhar e emitir um alerta de erro na infraestrutura. O repositório mantém-se contínuo, gerando a série temporal diária exata de que preciso, mas totalmente à prova de falhas de runtime em reexecuções no mesmo dia.
5. Distribuição de Dados: O fim da API Rest
Até aqui, resolvemos a automação da coleta (com GitHub Actions) e o armazenamento histórico (com Git-as-a-Database). Mas ainda faltava a última peça do quebra-cabeça: a distribuição.
Se eu não tenho um banco de dados tradicional rodando na nuvem, como o meu ERP Zootécnico (o Sistema de Gestão de Gado) vai consumir essas cotações atualizadas? A abordagem clássica ditaria que eu precisasse provisionar um servidor e subir uma API REST (usando Flask ou FastAPI) apenas para ler os arquivos JSON e devolver as respostas via HTTP. Ou seja, mais infraestrutura para monitorar e manter.
Foi aí que apliquei a regra de ouro da arquitetura minimalista: use o que já está pronto.
O GitHub possui uma rede de distribuição de conteúdo (CDN) global e nativa projetada para servir arquivos brutos. A cada commit que o robô faz atualizando o cotacoes_boi_hoje.json e o cotacoes_novilha_hoje.json, os dados ficam imediatamente disponíveis através do domínio raw.githubusercontent.com.
Na prática, eu transformei o próprio repositório em uma API estática. O meu sistema de gestão agora faz uma simples requisição HTTP GET direto para a URL Raw do GitHub. Ele consome o JSON mais recente de forma instantânea, servido por uma infraestrutura de altíssima disponibilidade bancada pela Microsoft, com custo absolutamente zero e sem precisar escrever uma única linha de código de rotas backend.
6. Conclusão: A Mentalidade FinOps
Todo projeto que eu me proponho a fazer me ensina algo. Um dos ensinamentos do Gado-Scraper foi: Nem sempre a solução mais complexa é a mais adequada. No final das contas, aquela limitação financeira citada no inicio do texto tornou esse projeto muito mais especial do que só um web scraper, foi toda uma jornada de busca de conhecimento para chegar nesse resultado satisfatório.
Quer dar uma olhada na pipeline YAML rodando ou usar o repositório como base para as suas próprias automações?
💻 Acesse o código-fonte: Dom1ng0s/Gado-Scraper no GitHub
Top comments (0)