This commit is contained in:
daniel.rodrigues 2025-11-21 12:56:39 -03:00
parent 87568422b4
commit c93ff700f4

View File

@ -16,7 +16,7 @@ import json
# Configuração da data
USE_MANUAL_DATE = False # False = usar dia anterior automaticamente; True = usar MANUAL_DATE_STR
MANUAL_DATE_STR = "16112025" # Formato DDMMAAAA, usado quando USE_MANUAL_DATE=True
MANUAL_DATE_STR = "20112025" # Formato DDMMAAAA, usado quando USE_MANUAL_DATE=True
# Configuração de intervalo de datas (execução dia por dia)
USE_DATE_RANGE = False # True = usar intervalo de datas; False = usar USE_MANUAL_DATE
@ -132,17 +132,20 @@ def main():
# Configurar opções do Chrome para ambiente Kubernetes/Docker
chrome_options = Options()
# CONFIGURAÇÃO: Defina como False para ver o navegador (útil para debug no Windows)
USE_HEADLESS = True # True = modo headless (sem interface), False = com interface
# Configurações essenciais para rodar em Docker/Kubernetes (sem interface gráfica)
chrome_options.add_argument('--headless') # Modo headless (sem interface gráfica)
if USE_HEADLESS:
chrome_options.add_argument('--headless=new') # Novo modo headless (mais estável)
chrome_options.add_argument('--no-sandbox') # Necessário para rodar como root
chrome_options.add_argument('--disable-dev-shm-usage') # Evita problemas de memória compartilhada
chrome_options.add_argument('--disable-gpu') # Desabilita GPU (não necessária em headless)
# Configurações adicionais recomendadas para Kubernetes
# Configurações adicionais recomendadas
chrome_options.add_argument('--disable-software-rasterizer')
chrome_options.add_argument('--disable-extensions')
chrome_options.add_argument('--disable-setuid-sandbox')
chrome_options.add_argument('--remote-debugging-port=9222')
chrome_options.add_argument('--disable-blink-features=AutomationControlled') # Evita detecção de automação
# Configurações de janela
chrome_options.add_argument('--window-size=1920,1080')
@ -150,7 +153,8 @@ def main():
# Desabilitar notificações e popups
chrome_options.add_argument('--disable-notifications')
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging', 'enable-automation'])
chrome_options.add_experimental_option('useAutomationExtension', False)
# Definir pasta de download
# Tenta usar Desktop, se não existir usa /tmp (para Kubernetes/Docker)
@ -178,13 +182,26 @@ def main():
# Inicializar o driver do Chrome
print("Iniciando o navegador...")
# Criar o service apontando para o chromedriver do sistema (Kubernetes/Docker)
# Comentar a linha abaixo se estiver rodando localmente no Windows
# service = Service('/usr/bin/chromedriver')
# driver = webdriver.Chrome(service=service, options=chrome_options)
print(f"Modo headless: {USE_HEADLESS}")
# Para rodar localmente no Windows, use:
driver = webdriver.Chrome(options=chrome_options)
try:
# Criar o service apontando para o chromedriver do sistema (Kubernetes/Docker)
# Comentar a linha abaixo se estiver rodando localmente no Windows
# from selenium.webdriver.chrome.service import Service
# service = Service('/usr/bin/chromedriver')
# driver = webdriver.Chrome(service=service, options=chrome_options)
# Para rodar localmente no Windows, use:
driver = webdriver.Chrome(options=chrome_options)
print("Navegador iniciado com sucesso!")
except Exception as e:
print(f"Erro ao iniciar o navegador: {e}")
print("\nPossíveis soluções:")
print("1. Verifique se o Chrome está instalado")
print("2. Verifique se o ChromeDriver está instalado e atualizado")
print("3. Execute: pip install --upgrade selenium")
print("4. Tente desabilitar o modo headless (USE_HEADLESS = False)")
raise
try:
# Acessar a URL
@ -442,13 +459,17 @@ def main():
]
}
try:
print("Abrindo arquivo CSV para leitura...")
# Detectar delimitador e ler
with open(downloaded_file, 'r', encoding='utf-8-sig', errors='ignore') as f:
print("Detectando delimitador do CSV...")
sample = f.read(4096)
f.seek(0)
try:
dialect = csv.Sniffer().sniff(sample, delimiters=';,\t|')
except Exception:
print(f"Delimitador detectado: '{dialect.delimiter}'")
except Exception as e:
print(f"Erro ao detectar delimitador, usando ';' por padrão: {e}")
class Simple(csv.Dialect):
delimiter = ';'
quotechar = '"'
@ -458,8 +479,11 @@ def main():
lineterminator = '\n'
quoting = csv.QUOTE_MINIMAL
dialect = Simple
print("Lendo cabeçalhos do CSV...")
reader = csv.DictReader(f, dialect=dialect)
original_headers = reader.fieldnames or []
print(f"Cabeçalhos encontrados: {original_headers}")
# Construir novos headers, removendo colunas e substituindo Produto
new_headers = []
@ -480,8 +504,15 @@ def main():
if 'SKU' not in new_headers:
new_headers.extend(['SKU', 'Descricao'])
print(f"Novos cabeçalhos processados: {new_headers}")
print("Processando linhas do CSV...")
rows_out = []
row_count = 0
for row in reader:
row_count += 1
if row_count % 100 == 0:
print(f"Processadas {row_count} linhas...")
new_row = {}
for h in original_headers:
nh = _normalize_label(h)
@ -508,7 +539,11 @@ def main():
new_row.setdefault('PRECO_MEDIO', '')
rows_out.append(new_row)
print(f"Total de linhas processadas: {row_count}")
print(f"Total de linhas válidas: {len(rows_out)}")
# Inserir os dados tratados no banco de dados
print("Preparando para inserir dados no banco...")
def _find_header(headers, targets):
targets_norm = { _normalize_label(t) for t in targets }
for hh in headers:
@ -542,11 +577,10 @@ def main():
print("Aviso: coluna de Preço Médio não encontrada; valores serão inseridos como 0.00.")
# Conectar ao SQL Server
print("Escolhendo driver SQL Server...")
driver_name = choose_sql_driver()
try:
available_drivers = pyodbc.drivers()
except Exception:
available_drivers = []
print(f"Driver selecionado: {driver_name}")
connection_string = (
f'DRIVER={{{driver_name}}};'
f'SERVER=10.77.77.10;'
@ -560,20 +594,28 @@ def main():
# Converter data para formato aceito pelo banco (date)
data_db = datetime.strptime(data_formatada, '%d%m%Y').date()
print(f"Data para inserção no banco: {data_db}")
print("Conectando ao banco de dados...")
inserted = 0
with pyodbc.connect(connection_string) as conn:
print("Conexão estabelecida com sucesso!")
conn.autocommit = False
cur = conn.cursor()
# Apagar dados existentes para a data
print(f"Deletando dados existentes para a data {data_db}...")
cur.execute("DELETE FROM [GINSENG].[dbo].[rgb_sales_selenium] WHERE [Data] = ?", data_db)
print("Dados antigos deletados.")
# Inserir linhas
print("Preparando inserção de dados...")
insert_sql = (
"INSERT INTO [GINSENG].[dbo].[rgb_sales_selenium] ([Data],[PDV],[SKU],[DESCRICAO],[VENDAS],[PRECO_MEDIO]) "
"VALUES (?,?,?,?,?,?)"
)
batch = []
batch_count = 0
for r in rows_out:
pdv_val = r.get(pdv_header, '')
try:
@ -591,26 +633,41 @@ def main():
pdv_val = None
batch.append((data_db, pdv_val, sku_val, desc_val, vendas_val, preco_medio_val))
if len(batch) >= 1000:
batch_count += 1
print(f"Inserindo lote {batch_count} ({len(batch)} registros)...")
cur.executemany(insert_sql, batch)
inserted += len(batch)
batch = []
if batch:
batch_count += 1
print(f"Inserindo lote final {batch_count} ({len(batch)} registros)...")
cur.executemany(insert_sql, batch)
inserted += len(batch)
print("Fazendo commit das alterações...")
conn.commit()
print("Commit realizado com sucesso!")
print(f"Dados inseridos no banco: {inserted} registros para a data {data_db}.")
# Remover arquivo original
print(f"Removendo arquivo temporário: {downloaded_file}")
try:
os.remove(downloaded_file)
except Exception:
pass
print("Arquivo removido com sucesso.")
except Exception as e:
print(f"Aviso: não foi possível remover o arquivo: {e}")
# Salvar estado apenas após sucesso completo
if USE_DATE_RANGE:
print("Salvando estado do processamento...")
_save_date_state(data_formatada)
print("Processamento concluído com sucesso!")
except Exception as e:
print(f"Falha ao tratar o CSV: {e}")
import traceback
traceback.print_exc()
else:
print("Não foi possível detectar o arquivo baixado dentro do tempo limite.")