""" Google Ads Generator - Interface Streamlit Aplicação que extrai conteúdo de Landing Pages e gera automaticamente ativos de campanha para Google Ads usando IA (OpenAI ou Gemini). """ import os import re import streamlit as st from dotenv import load_dotenv from src.scraper import scrape_landing_page from src.ai_generator import generate_google_ads_assets, MODELS from src.exporter import ( create_keywords_df, create_negative_keywords_df, create_ads_df, create_sitelinks_df, create_callouts_df, export_all_to_excel, ) # ─── Carregar variáveis de ambiente ─────────────────────────────── load_dotenv() def _highlight_keywords(text: str, keywords: list[str]) -> str: """Destaca em negrito as palavras-chave encontradas no texto.""" result = text for kw in keywords: pattern = re.compile(re.escape(kw), re.IGNORECASE) match = pattern.search(result) if match: result = result[:match.start()] + f"**{match.group()}**" + result[match.end():] break # Destacar apenas a primeira keyword encontrada return result # ─── Configuração da Página ─────────────────────────────────────── st.set_page_config( page_title="Google Ads Generator", page_icon="📊", layout="wide", ) # ─── CSS Customizado ────────────────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) # ─── Header ─────────────────────────────────────────────────────── st.markdown('
Google Ads Generator
', unsafe_allow_html=True) st.markdown( 'Gere ativos de campanha automaticamente a partir da sua Landing Page
', unsafe_allow_html=True, ) # ─── Sidebar ────────────────────────────────────────────────────── with st.sidebar: st.header("Configurações") provider = st.selectbox( "Provider de IA", ["OpenAI", "Gemini"], index=0, help="Escolha o provider de IA. As chaves devem estar configuradas no arquivo .env", ) model = st.selectbox( "Modelo", MODELS.get(provider, []), index=0, help="Modelo a ser utilizado para geração dos ativos.", ) # Status das chaves configuradas st.divider() openai_ok = bool(os.environ.get("OPENAI_API_KEY")) gemini_ok = bool(os.environ.get("GEMINI_API_KEY")) st.caption("Status das API Keys (.env):") st.markdown(f"- OpenAI: {'✅ Configurada' if openai_ok else '❌ Não encontrada'}") st.markdown(f"- Gemini: {'✅ Configurada' if gemini_ok else '❌ Não encontrada'}") st.divider() campaign_name = st.text_input( "Nome da Campanha", value="Campanha LP", help="Nome que aparecerá na coluna 'Campaign' do CSV.", ) ad_group = st.text_input( "Nome do Grupo de Anúncios", value="Grupo 1", help="Nome que aparecerá na coluna 'Ad Group' do CSV.", ) st.divider() st.caption("Desenvolvido com Streamlit + OpenAI + Gemini") # ─── Área Principal ─────────────────────────────────────────────── url = st.text_input( "URL da Landing Page", placeholder="https://www.seusite.com.br/landing-page", help="Cole a URL completa da sua Landing Page aqui.", ) col_btn, col_status = st.columns([1, 3]) with col_btn: generate_btn = st.button("Gerar Campanha", type="primary", use_container_width=True) # ─── Lógica Principal ──────────────────────────────────────────── if generate_btn: # Validações if not url: st.error("Por favor, insira a URL da Landing Page.") st.stop() if not url.startswith(("http://", "https://")): st.error("A URL deve começar com http:// ou https://") st.stop() # Verificar se a chave do provider selecionado está configurada if provider == "OpenAI" and not os.environ.get("OPENAI_API_KEY"): st.error("OPENAI_API_KEY não encontrada no arquivo .env. Configure antes de continuar.") st.stop() elif provider == "Gemini" and not os.environ.get("GEMINI_API_KEY"): st.error("GEMINI_API_KEY não encontrada no arquivo .env. Configure antes de continuar.") st.stop() # Step 1: Scraping with st.status("Processando...", expanded=True) as status: st.write("Extraindo conteúdo da Landing Page...") try: lp_data = scrape_landing_page(url) except Exception as e: st.error(f"Erro ao acessar a URL: {e}") st.stop() st.write(f"Conteúdo extraído: {len(lp_data['paragraphs'])} parágrafos, " f"{len(lp_data['h1'])} H1, {len(lp_data['h2'])} H2, " f"{len(lp_data.get('list_items', []))} itens de lista, " f"{len(lp_data['ctas'])} CTAs") # Step 2: IA st.write(f"Gerando ativos com {provider} ({model})...") try: assets, prompts = generate_google_ads_assets( lp_content=lp_data["full_text"], provider=provider, model=model, ) except ValueError as e: st.error(str(e)) st.stop() except Exception as e: st.error(f"Erro ao gerar ativos: {e}") st.stop() status.update(label="Concluído!", state="complete", expanded=False) # Salvar no session_state st.session_state["assets"] = assets st.session_state["prompts"] = prompts st.session_state["lp_data"] = lp_data st.session_state["campaign_name"] = campaign_name st.session_state["ad_group"] = ad_group st.session_state["provider_used"] = provider st.session_state["model_used"] = model # ─── Exibição dos Resultados ───────────────────────────────────── if "assets" in st.session_state: assets = st.session_state["assets"] prompts = st.session_state.get("prompts", {}) camp = st.session_state.get("campaign_name", "Campanha LP") adg = st.session_state.get("ad_group", "Grupo 1") prov_used = st.session_state.get("provider_used", "") model_used = st.session_state.get("model_used", "") st.divider() # Métricas resumo col1, col2, col3, col4, col5, col6 = st.columns(6) with col1: st.metric("Keywords", len(assets.get("keywords", []))) with col2: st.metric("Negativas", len(assets.get("negative_keywords", []))) with col3: st.metric("Títulos", len(assets.get("headlines", []))) with col4: st.metric("Descrições", len(assets.get("descriptions", []))) with col5: st.metric("Sitelinks", len(assets.get("sitelinks", []))) with col6: st.metric("Callouts", len(assets.get("callouts", []))) st.divider() # Abas com resultados tab_kw, tab_neg, tab_ads, tab_sl, tab_co, tab_prompts, tab_raw = st.tabs([ "Keywords", "Negativas", "Anúncios RSA", "Sitelinks", "Callouts", "Prompts Utilizados", "Dados da LP", ]) with tab_kw: st.subheader("Palavras-chave") kw_df = create_keywords_df(assets, camp, adg) if not kw_df.empty: st.dataframe(kw_df, use_container_width=True, hide_index=True) else: st.info("Nenhuma palavra-chave gerada.") with tab_neg: st.subheader("Palavras-chave Negativas") nkw_df = create_negative_keywords_df(assets, camp) if not nkw_df.empty: st.dataframe(nkw_df, use_container_width=True, hide_index=True) else: st.info("Nenhuma palavra-chave negativa gerada.") with tab_ads: st.subheader("Anúncio Responsivo de Pesquisa (RSA)") st.write("**Títulos (Headlines):**") # Coletar palavras-chave para destacar nos títulos kw_list = sorted( [kw["keyword"].lower() for kw in assets.get("keywords", []) if kw.get("keyword")], key=len, reverse=True, # Maior primeiro para evitar match parcial ) for i, h in enumerate(assets.get("headlines", []), 1): char_count = len(h) color = "green" if char_count <= 30 else "red" h_highlighted = _highlight_keywords(h, kw_list) st.markdown(f"{i}. {h_highlighted} — :{color}[{char_count} chars]") st.write("**Descrições:**") for i, d in enumerate(assets.get("descriptions", []), 1): char_count = len(d) color = "green" if char_count <= 90 else "red" st.markdown(f"{i}. {d} — :{color}[{char_count} chars]") st.divider() ads_df = create_ads_df(assets, camp, adg) if not ads_df.empty: st.dataframe(ads_df, use_container_width=True, hide_index=True) with tab_sl: st.subheader("Sitelinks") sl_df = create_sitelinks_df(assets, camp) if not sl_df.empty: st.dataframe(sl_df, use_container_width=True, hide_index=True) else: st.info("Nenhum sitelink gerado.") with tab_co: st.subheader("Callouts (Frases de Destaque)") co_df = create_callouts_df(assets, camp) if not co_df.empty: st.dataframe(co_df, use_container_width=True, hide_index=True) else: st.info("Nenhum callout gerado.") with tab_prompts: st.subheader("Prompts Utilizados") st.caption(f"Provider: **{prov_used}** | Modelo: **{model_used}**") st.markdown("#### Prompt de Sistema (System Prompt)") st.code(prompts.get("system_prompt", "N/A"), language=None) st.markdown("#### Prompt do Usuário (User Prompt)") st.code(prompts.get("user_prompt", "N/A"), language=None) with tab_raw: st.subheader("Dados Extraídos da Landing Page") lp_data = st.session_state.get("lp_data", {}) if lp_data: st.write(f"**URL:** {lp_data.get('url', '')}") st.write(f"**Título:** {lp_data.get('title', '')}") st.write(f"**Meta Description:** {lp_data.get('meta_description', '')}") with st.expander("Headings (H1, H2, H3)"): for h in lp_data.get("h1", []): st.write(f"**H1:** {h}") for h in lp_data.get("h2", []): st.write(f"**H2:** {h}") for h in lp_data.get("h3", []): st.write(f"**H3:** {h}") with st.expander("Itens de Lista / Benefícios"): for li in lp_data.get("list_items", []): st.write(f"- {li}") with st.expander("CTAs Encontrados"): for c in lp_data.get("ctas", []): st.write(f"- {c}") with st.expander("Texto Completo Enviado à IA"): st.code(lp_data.get("full_text", ""), language=None) # ─── Botões de Download ─────────────────────────────────────── st.divider() st.subheader("Download dos Ativos") col_dl1, col_dl2 = st.columns(2) with col_dl1: excel_data = export_all_to_excel(assets, camp, adg) st.download_button( label="Baixar Excel (.xlsx)", data=excel_data, file_name="google_ads_assets.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", use_container_width=True, ) with col_dl2: kw_csv = create_keywords_df(assets, camp, adg).to_csv(index=False) st.download_button( label="Baixar Keywords (.csv)", data=kw_csv, file_name="google_ads_keywords.csv", mime="text/csv", use_container_width=True, )