import pandas as pd
import requests
from pathlib import Path
from datetime import datetime
from io import StringIO, BytesIO
from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_exception_type
import os
# --- CONFIGURAÇÕES ---
PASTA_SAIDA = Path('dados_salvos')
NOME_BASE = 'dados'
VERSOES_MANTIDAS = 3
# --- GARANTE QUE A PASTA DE SAÍDA EXISTE E É ACESSÍVEL ---
def criar_pasta_saida():
PASTA_SAIDA.mkdir(exist_ok=True)
if not os.access(PASTA_SAIDA, os.W_OK):
raise PermissionError(f'Sem permissão de escrita em {PASTA_SAIDA}')
# --- DETECTA O TIPO DO CONTEÚDO COM BASE NO HEADER E EXTENSÃO DA URL ---
def identificar_tipo_conteudo(url):
try:
with requests.get(url, stream=True, timeout=15) as r:
r.raise_for_status()
ct = r.headers.get('Content-Type', '').lower()
if 'html' in ct: return 'html'
if 'xml' in ct: return 'xml'
if 'json' in ct: return 'json'
if 'csv' in ct: return 'csv'
if 'excel' in ct or url.lower().endswith(('.xls', '.xlsx')): return 'excel'
if url.lower().endswith('.xml'): return 'xml'
if url.lower().endswith('.json'): return 'json'
if url.lower().endswith('.csv'): return 'csv'
if url.lower().endswith(('.html', '.htm')): return 'html'
return 'desconhecido'
except Exception as e:
print(f'Erro ao identificar tipo: {e}')
return 'erro'
# --- FAZ O DOWNLOAD E CARREGAMENTO DOS DADOS, COM TENTATIVAS AUTOMÁTICAS EM CASO DE ERRO ---
@retry(wait=wait_fixed(3), stop=stop_after_attempt(3), retry=retry_if_exception_type(requests.RequestException))
def baixar_e_carregar_dados(url, tipo):
with requests.get(url, timeout=20) as r:
r.raise_for_status()
# Tratamento especial para HTML com múltiplas tabelas
if tipo == 'html':
tabelas = pd.read_html(StringIO(r.text))
print(f'Foram encontradas {len(tabelas)} tabelas na página.\n')
for i, tabela in enumerate(tabelas):
print(f'Tabela {i}: {tabela.shape[0]} linhas x {tabela.shape[1]} colunas')
print(tabela.head(3), '\n')
while True:
try:
escolha = int(input(f'Digite o número da tabela que deseja salvar (0 a {len(tabelas)-1}): '))
if 0 <= escolha < len(tabelas):
return tabelas[escolha]
else:
print('Número inválido.')
except ValueError:
print('Entrada inválida. Digite um número inteiro.')
buffer = BytesIO(r.content)
if tipo == 'excel':
return pd.read_excel(buffer)
if tipo == 'xml':
return pd.read_xml(buffer)
if tipo == 'csv':
return pd.read_csv(buffer)
if tipo == 'json':
return pd.read_json(buffer)
print(f"Tipo '{tipo}' não suportado.")
return None
# --- VERIFICA SE OS DADOS CARREGADOS SÃO VÁLIDOS ---
def validar_dados(df):
if df is None or df.empty or df.shape[1] == 0:
return False
print(f'Colunas detectadas: {list(df.columns)}')
return True
# --- SALVA A TABELA ESCOLHIDA EM CSV E HTML ---
def salvar_versao(df):
ts = datetime.now().strftime('%Y%m%d_%H%M%S')
df.to_csv(PASTA_SAIDA / f'{NOME_BASE}_{ts}.csv', index=False)
df.to_html(PASTA_SAIDA / f'{NOME_BASE}_{ts}.html', index=False)
print(f'Dados salvos: {NOME_BASE}_{ts}.csv/html')
limpar_versoes_antigas()
# --- REMOVE VERSÕES ANTIGAS, MANTENDO SOMENTE AS MAIS RECENTES ---
def limpar_versoes_antigas():
arquivos = sorted(PASTA_SAIDA.glob(f'{NOME_BASE}_*.csv'), reverse=True)
for arq in arquivos[VERSOES_MANTIDAS:]:
arq.unlink(missing_ok=True)
html = arq.with_suffix('.html')
if html.exists(): html.unlink()
print(f'Removido: {arq.name}, {html.name}')
# --- EXECUTA O PROCESSO COMPLETO PARA UMA URL ---
def executar(url):
print('Inicio da execução')
try:
criar_pasta_saida()
tipo = identificar_tipo_conteudo(url)
print(f'Tipo detectado: {tipo}')
df = baixar_e_carregar_dados(url, tipo)
if validar_dados(df):
print('\nResumo dos dados:')
df.info(verbose=False)
salvar_versao(df)
print('\nDados salvos com sucesso. Primeiras linhas:')
print(df.head())
else:
print('Dados inválidos.')
except Exception as e:
print(f'Erro durante execução: {e}')
# --- EXEMPLO DE EXECUÇÃO ---
if __name__ == '__main__':
url_exemplo = 'https://pt.wikipedia.org/wiki/Lista_de_pa%C3%ADses_por_popula%C3%A7%C3%A3o'
executar(url_exemplo)