diff --git a/extracao_vendashora_rgb.py b/extracao_vendashora_rgb.py index 05d35f9..8b6fc82 100644 --- a/extracao_vendashora_rgb.py +++ b/extracao_vendashora_rgb.py @@ -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.")