Nossa missão é fornecer o melhor ensino em engenharia de dados
Bem-vindo a Jornada de Dados
Aqui está o README atualizado, agora usando a API da Bitcoin na Coinbase como referência:
Esquema do projeto: app.excalidraw.com
Este projeto faz parte de um workshop gratuito de Data Engineering para Iniciantes, focado na criação de pipelines de dados ETL (Extract, Transform, Load). O objetivo é construir um programa que consome dados de uma API (Coinbase), organiza esses dados e armazena em uma base de dados.
Você aprenderá conceitos fundamentais de Engenharia de Dados, como:
- O que é uma API e como consumi-la usando Python.
- O processo completo de ETL (extração, transformação e carga).
- Como automatizar a execução do pipeline para coleta contínua.
Ao final do projeto, você terá um programa funcional que:
- Coleta o preço atual da Bitcoin em tempo real.
- Salva esses dados em um formato tabular para futura análise.
Desenvolver um pipeline ETL automatizado para consumir dados da API da Coinbase e armazenar informações sobre o preço da Bitcoin ao longo do tempo.
-
Extração (E):
- Utilizar a API da Coinbase para obter o preço atual da Bitcoin.
- Trabalhar com os endpoints públicos da API (sem necessidade de autenticação).
-
Transformação (T):
- Selecionar apenas as informações relevantes: preço da Bitcoin, horário da consulta e moeda de referência (USD).
- Organizar os dados no formato tabular utilizando Pandas.
-
Carga (L):
- Armazenar os dados coletados em um arquivo CSV ou em um banco de dados SQLite.
-
Automatização:
- Agendar o programa para executar periodicamente (por exemplo, a cada hora ou diariamente), garantindo a coleta contínua dos dados.
- Python 3.12
- Bibliotecas:
requests
: Para consumir APIs.pandas
: Para manipulação e organização de dados.sqlite3
: Para armazenamento em banco de dados (opcional).tinydb
: Para armazenamento em banco de dados NoSQL.sqlalchemy
: SQLAlchemy é uma biblioteca de mapeamento objeto-relacional para Python.psycopg2-binary
: Psycopg é uma biblioteca de acesso a dados PostgreSQL para Python.streamlit
: Para criar dashboards interativos.time
: Para medir o tempo de execução do programa.datetime
: Para manipulação de datas e horas.
- Coinbase API: Para obter o preço da Bitcoin em tempo real.
Data/Hora | Preço (USD) | Moeda |
---|---|---|
2024-01-01 12:00:00 | 42,000.50 | Bitcoin |
2024-01-01 13:00:00 | 42,150.75 | Bitcoin |
Ao final deste projeto, você será capaz de:
- Extrair dados em tempo real de APIs públicas.
- Transformar e organizar os dados em formato estruturado.
- Automatizar o pipeline ETL para coleta recorrente dos dados.
Exemplo de Análises Futuras:
- Monitorar o preço da Bitcoin ao longo do tempo.
- Identificar padrões de variação diária, semanal ou mensal.
- Criar alertas para valores mínimos/máximos.
Este projeto é apenas o começo. Nos próximos módulos, cobriremos:
- Transformação Avançada: Limpeza e enriquecimento dos dados.
- Armazenamento Persistente: Introdução a bancos de dados em nuvem.
- Visualização de Dados: Construção de dashboards interativos.
-
Clone o Repositório:
git clone https://github.com/seu-usuario/data-pipeline-bitcoin.git cd data-pipeline-bitcoin
-
Instale as Dependências:
pip install requests pandas
-
Execute o Script:
python main.py
-
Verifique os Dados:
- O arquivo
bitcoin_prices.csv
será gerado com os preços coletados.
- O arquivo
Agora, mãos à obra! 🚀
Essa versão usa a API pública da Coinbase e adapta o fluxo do projeto para a captura do preço da Bitcoin. Ela é simples, funcional e ideal para iniciantes em Engenharia de Dados!
A grande diferença entre o consumo de memória do Selenium e o consumo de memória do requests é resultado da complexidade e funcionamento interno das duas abordagens. Vamos analisar ponto a ponto:
-
Selenium:
- Selenium abre um navegador real, como o Google Chrome.
- O ChromeDriver inicializa uma instância completa do navegador, que carrega:
- HTML completo,
- CSS, JavaScript dinâmico,
- Imagens e outros recursos visuais.
- O navegador consome memória como qualquer navegador que você usaria manualmente.
- O processo principal Python apenas controla o ChromeDriver, mas a maior parte da memória é consumida pelo processo filho (navegador).
-
Requests:
- A biblioteca
requests
faz apenas uma requisição HTTP simples ao servidor. - Ele não carrega imagens, CSS, JavaScript ou renderiza nada. Apenas recebe o HTML cru ou JSON como texto e o processa.
- Como resultado, o consumo de memória é extremamente baixo, pois não há dependências complexas ou renderização.
- A biblioteca
🔍 Memória inicial: 33.39 MB
📈 Memória após abrir o navegador: 587.02 MB
📈 Memória após carregar a página: 967.70 MB
💰 Preço atual do Bitcoin (InfoMoney): 655.084,00
📈 Memória após captura do preço: 963.44 MB
🔻 Memória final após fechar o navegador: 21.14 MB
🚀 Pico de memória do processo Python: 0.56 MB
Explicação:
- 33 MB: Memória inicial do Python, apenas carregando o interpretador e bibliotecas.
- 587 MB: O navegador foi aberto, e ele sozinho consome cerca de 500 MB para funcionar.
- 967 MB: A página foi carregada com todos os recursos (scripts, imagens, etc.).
- Pico do Python (0.56 MB): O Selenium apenas controla o navegador, mas não aloca muito dentro do Python. O navegador Chrome (processo filho) consome a maior parte dos recursos.
Pico de memória durante a execução: 0.17 MB
Explicação:
- A biblioteca
requests
apenas faz uma requisição leve e recebe uma resposta. - Como não há renderização ou recursos pesados sendo carregados, o consumo de memória é mínimo.
- O pico de memória é extremamente baixo porque o Python armazena apenas o JSON (ou HTML) da resposta, que ocupa poucos KBs.
Aspecto | Selenium | Requests |
---|---|---|
Execução | Abre um navegador completo (Chrome). | Apenas faz uma requisição. |
Carregamento | Renderiza HTML, CSS, JavaScript, etc. | Processa apenas o texto. |
Processo Controlado | Processos filhos consomem memória. | Apenas processo Python. |
Complexidade | Mais pesado devido ao ChromeDriver. | Muito leve e direto. |
Pico de Memória Python | Baixo: Python controla o ChromeDriver. | Leve: Apenas resposta HTTP. |
-
Selenium:
- É muito mais pesado porque ele abre e controla um navegador completo.
- O ChromeDriver e o navegador Chrome (processos filhos) consomem quase 1 GB de RAM.
-
Requests:
- É muito mais leve porque apenas faz uma requisição HTTP.
- Não há renderização ou carregamento de recursos extras, então o consumo é quase insignificante.
-
Use Requests:
- Quando o site oferece uma API ou os dados estão disponíveis diretamente no HTML sem necessidade de renderização dinâmica.
-
Use Selenium:
- Quando o site carrega conteúdo com JavaScript ou é necessário interagir com o navegador (cliques, scroll, etc.).
Se precisar otimizar ou escolher a melhor abordagem para um caso específico, é só avisar! 🚀
Quando você entra em um site ou consome uma API, a comunicação entre o cliente (navegador ou aplicativo) e o servidor ocorre por meio de requisições e respostas. A diferença principal está no conteúdo enviado e recebido.
Aspecto | Site | API |
---|---|---|
Requisição | Feita pelo navegador (HTTP GET/POST). | Feita pelo cliente (aplicação/script) via HTTP. |
Resposta do Servidor | HTML, CSS, JavaScript e recursos visuais (imagens, vídeos). | Dados estruturados (geralmente JSON ou XML). |
Renderização | Navegador renderiza a página (front-end visual). | Cliente processa os dados e decide o uso. |
Uso | Visualização para um usuário humano. | Integração entre sistemas ou aplicações. |
-
Site:
- O cliente (navegador) envia uma requisição HTTP GET para acessar a URL de um site.
- O servidor responde com um HTML que contém links para CSS, JavaScript e imagens.
- O navegador renderiza todos esses arquivos para exibir a página.
-
API:
- O cliente (aplicação) envia uma requisição HTTP GET/POST para a URL da API.
- O servidor responde apenas com dados estruturados (exemplo: JSON ou XML).
- Não há renderização visual; a resposta é processada diretamente pelo código.
Aqui está o fluxo dos dois casos no formato Mermaid.
sequenceDiagram
participant Cliente as Navegador (Cliente)
participant Servidor as Servidor Web
Cliente->>Servidor: HTTP GET (Requisição para URL do site)
Servidor-->>Cliente: HTML, CSS, JS, Imagens (Resposta)
Cliente->>Cliente: Renderiza HTML e carrega recursos
sequenceDiagram
participant Cliente as Aplicação (Cliente)
participant Servidor as Servidor de API
Cliente->>Servidor: HTTP GET/POST (Requisição para endpoint)
Servidor-->>Cliente: JSON ou XML (Resposta)
Cliente->>Cliente: Processa os dados retornados
-
Site:
- O servidor envia um HTML que o navegador processa e renderiza.
- O HTML referencia CSS, JavaScript, e imagens, que são carregados separadamente.
-
API:
- O servidor retorna apenas dados estruturados em JSON/XML.
- A aplicação cliente consome e processa esses dados diretamente, sem renderização visual.
Se quiser, posso ajustar o fluxo para adicionar mais detalhes ou exemplos práticos. 🚀
Vamos usar o Postgres, que é um banco de dados open source, gratuito e muito popular.
Para instalar o Postgres, você pode usar o Docker, que é uma ferramenta que facilita a execução de contêineres de software.
Ou você pode usar o servidor de banco de dados da Render ou Azure que é um serviço de cloud computing que facilita a execução de contêineres de software.
A Render é uma plataforma de cloud computing que facilita a execução de contêineres de software.
Para criar um servidor de banco de dados na Render, você pode usar o link: Render
Clique em New e Postgres
Coloque o nome do seu banco de dados, o usuário e a senha.
Esse arquivo é responsável por criar a tabela no banco de dados.
O ORM (Object-Relational Mapping) é uma técnica que permite mapear objetos de um programa para tabelas de um banco de dados.
Dessa forma, você não precisa mais usar SQL puro para criar e manipular a tabela.
class BitcoinPreco(Base):
"""Define a tabela no banco de dados."""
__tablename__ = "bitcoin_precos"
id = Column(Integer, primary_key=True, autoincrement=True)
valor = Column(Float, nullable=False)
criptomoeda = Column(String(50), nullable=False) # até 50 caracteres
moeda = Column(String(10), nullable=False) # até 10 caracteres
timestamp = Column(DateTime, default=datetime.now)
A classe BitcoinPreco
define as colunas para uma tabela chamada bitcoin_precos
no PostgreSQL. Aqui vai o SQL bruto que você pode usar para criar a tabela manualmente no PostgreSQL:
CREATE TABLE IF NOT EXISTS bitcoin_precos (
id SERIAL PRIMARY KEY,
valor DOUBLE PRECISION NOT NULL,
criptomoeda VARCHAR(50) NOT NULL,
moeda VARCHAR(10) NOT NULL,
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
Essa instrução corresponde à estrutura modelada em BitcoinPreco
pelo SQLAlchemy. Se você quiser adequar o tamanho de cada VARCHAR
ou usar outro tipo como TEXT
, fique à vontade para ajustar conforme suas necessidades.
import os
import time
import requests
from datetime import datetime
from dotenv import load_dotenv
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# Importar Base e BitcoinPreco do database.py
from database import Base, BitcoinPreco
# Carrega variáveis de ambiente do arquivo .env
load_dotenv()
# Lê as variáveis separadas do arquivo .env (sem SSL)
POSTGRES_USER = os.getenv("POSTGRES_USER")
POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD")
POSTGRES_HOST = os.getenv("POSTGRES_HOST")
POSTGRES_PORT = os.getenv("POSTGRES_PORT")
POSTGRES_DB = os.getenv("POSTGRES_DB")
# Monta a URL de conexão ao banco PostgreSQL (sem ?sslmode=...)
DATABASE_URL = (
f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}"
f"@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}"
)
# Cria o engine e a sessão
engine = create_engine(DATABASE_URL)
Session = sessionmaker(bind=engine)
def criar_tabela():
"""Cria a tabela no banco de dados, se não existir."""
Base.metadata.create_all(engine)
print("Tabela criada/verificada com sucesso!")
def extrair_dados_bitcoin():
"""Extrai o JSON completo da API da Coinbase."""
url = 'https://api.coinbase.com/v2/prices/spot'
resposta = requests.get(url)
if resposta.status_code == 200:
return resposta.json()
else:
print(f"Erro na API: {resposta.status_code}")
return None
def tratar_dados_bitcoin(dados_json):
"""Transforma os dados brutos da API e adiciona timestamp."""
valor = float(dados_json['data']['amount'])
criptomoeda = dados_json['data']['base']
moeda = dados_json['data']['currency']
timestamp = datetime.now()
dados_tratados = {
"valor": valor,
"criptomoeda": criptomoeda,
"moeda": moeda,
"timestamp": timestamp
}
return dados_tratados
def salvar_dados_postgres(dados):
"""Salva os dados no banco PostgreSQL."""
session = Session()
novo_registro = BitcoinPreco(**dados)
session.add(novo_registro)
session.commit()
session.close()
print(f"[{dados['timestamp']}] Dados salvos no PostgreSQL!")
if __name__ == "__main__":
criar_tabela()
print("Iniciando ETL com atualização a cada 15 segundos... (CTRL+C para interromper)")
while True:
try:
dados_json = extrair_dados_bitcoin()
if dados_json:
dados_tratados = tratar_dados_bitcoin(dados_json)
print("Dados Tratados:", dados_tratados)
salvar_dados_postgres(dados_tratados)
time.sleep(15)
except KeyboardInterrupt:
print("\nProcesso interrompido pelo usuário. Finalizando...")
break
except Exception as e:
print(f"Erro durante a execução: {e}")
time.sleep(15)
Neste caso, seu arquivo .env
também não conterá SSL:
POSTGRES_USER=jornadadedados
POSTGRES_PASSWORD=mudar123
POSTGRES_HOST=bancodedadospostgres.postgres.database.azure.com
POSTGRES_PORT=5432
POSTGRES_DB=postgres
database.py
: contém apenas a definição deBase
e do modeloBitcoinPreco
.exemplo_05.py
(ou outro nome principal): faz o ETL, cria a tabela usandoBase
, e salva os dados usando a instância daSession
.
Com isso, você removeu completamente a parte de SSL.