Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| from bs4 import BeautifulSoup | |
| import pandas as pd | |
| from huggingface_hub import InferenceClient | |
| import os | |
| import json # Importiamo la libreria per gestire il formato JSON | |
| # Legge il token dai secrets dello Space | |
| HF_TOKEN = os.environ.get("HF_TOKEN") | |
| # Inizializza il client per l'API di Hugging Face | |
| client = InferenceClient( | |
| model="mistralai/Mixtral-8x7B-Instruct-v0.1", | |
| token=HF_TOKEN, | |
| ) | |
| def clean_text(html_content): | |
| """Estrae e pulisce il testo dall'HTML.""" | |
| soup = BeautifulSoup(html_content, "html.parser") | |
| for element in soup(["script", "style", "header", "footer", "nav", "aside"]): | |
| element.decompose() | |
| text = soup.get_text(separator=' ', strip=True) | |
| return ' '.join(text.split()) | |
| def scrape_urls(urls): | |
| """Scansiona una lista di URL e restituisce il testo concatenato.""" | |
| all_text = "" | |
| headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'} | |
| for url in urls: | |
| try: | |
| response = requests.get(url.strip(), headers=headers, timeout=10) | |
| response.raise_for_status() | |
| all_text += clean_text(response.text) + " " | |
| except requests.RequestException as e: | |
| print(f"Errore durante lo scraping di {url}: {e}") | |
| return all_text | |
| def classify_keywords(urls_text, keywords_text, progress=gr.Progress()): | |
| """Funzione principale che orchestra lo scraping e la classificazione.""" | |
| if not HF_TOKEN: | |
| raise gr.Error("Token Hugging Face non trovato! Assicurati di averlo inserito nei Secrets dello Space.") | |
| urls = [url.strip() for url in urls_text.split('\n') if url.strip()] | |
| keywords = list(set([kw.strip() for kw in keywords_text.split('\n') if kw.strip()])) | |
| if not urls or not keywords: | |
| raise gr.Error("Per favore, inserisci almeno un URL e una lista di keyword.") | |
| # 1. Scraping | |
| progress(0.1, desc="Fase 1/3: Sto analizzando il contenuto degli URL...") | |
| website_content = scrape_urls(urls) | |
| if not website_content: | |
| raise gr.Error("Impossibile estrarre contenuto dagli URL forniti. Controlla che siano validi e accessibili.") | |
| website_content = website_content[:15000] | |
| # 2. Riassunto del contesto | |
| progress(0.3, desc="Fase 2/3: Sto capendo l'argomento principale del sito...") | |
| messages_summary = [ | |
| {"role": "user", "content": f"""Analizza il seguente testo estratto da un sito web e riassumi in 2-3 frasi i principali prodotti, servizi e le categorie tematiche trattate. Sii conciso e focalizzati su ciò che il sito vende o promuove. Testo del sito: "{website_content}" """} | |
| ] | |
| try: | |
| response_summary = client.chat_completion(messages_summary, max_tokens=200) | |
| website_summary = response_summary.choices[0].message.content.strip() | |
| except Exception as e: | |
| raise gr.Error(f"Errore nella chiamata al modello per il riassunto: {e}") | |
| # 3. Classificazione di TUTTE le keyword in un'unica chiamata (BATCH) | |
| progress(0.6, desc="Fase 3/3: Sto classificando tutte le keyword in un colpo solo...") | |
| # Formatta la lista di keyword per il prompt | |
| keyword_list_str = "\n- ".join(keywords) | |
| messages_classify = [ | |
| {"role": "user", "content": f"""Contesto del sito: "{website_summary}" | |
| Data la lista di keyword qui sotto, classifica ciascuna di esse come "Pertinente" o "Non pertinente" rispetto al contesto del sito. | |
| IMPORTANTE: Rispondi ESCLUSIVAMENTE con un array JSON valido. Ogni elemento dell'array deve essere un oggetto con due chiavi: "keyword" e "classification". | |
| Esempio di output corretto: | |
| [ | |
| {{"keyword": "piastrelle bagno", "classification": "Pertinente"}}, | |
| {{"keyword": "ricetta della pizza", "classification": "Non pertinente"}} | |
| ] | |
| Ecco la lista di keyword da classificare: | |
| - {keyword_list_str} | |
| """} | |
| ] | |
| results = [] | |
| try: | |
| # Aumentiamo i token massimi per contenere la risposta JSON | |
| response = client.chat_completion(messages_classify, max_tokens=4096, temperature=0.1) | |
| response_text = response.choices[0].message.content.strip() | |
| # Pulisce la risposta per estrarre solo il JSON | |
| json_start = response_text.find('[') | |
| json_end = response_text.rfind(']') + 1 | |
| clean_json_str = response_text[json_start:json_end] | |
| results = json.loads(clean_json_str) | |
| except json.JSONDecodeError: | |
| raise gr.Error(f"Il modello ha restituito un formato JSON non valido. Riprova. Risposta ricevuta: {response_text}") | |
| except Exception as e: | |
| raise gr.Error(f"Errore durante la classificazione in batch delle keyword: {e}") | |
| df = pd.DataFrame(results) | |
| csv_path = "classificazione_keyword.csv" | |
| df.to_csv(csv_path, index=False) | |
| return df, csv_path, f"**Contesto Rilevato:**\n{website_summary}" | |
| # --- Interfaccia Grafica con Gradio (invariata) --- | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# Strumento di Analisi Pertinenza Keyword") | |
| gr.Markdown("Inserisci uno o più URL e una lista di keyword per classificarle in base al contenuto dei siti web.") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| urls_input = gr.Textbox(lines=5, label="URL da Analizzare", placeholder="https://www.esempio.it\nhttps://www.esempio.it/prodotti") | |
| keywords_input = gr.Textbox(lines=15, label="Keyword da Classificare", placeholder="keyword 1\nkeyword 2\nkeyword 3...") | |
| with gr.Column(scale=2): | |
| summary_output = gr.Markdown("Il riassunto del sito apparirà qui...") | |
| df_output = gr.DataFrame(headers=["Keyword", "Classificazione"], datatype=["str", "str"], label="Risultati Classificazione") | |
| file_output = gr.File(label="Scarica Risultati in formato CSV") | |
| analyze_button = gr.Button("Analizza Pertinenza", variant="primary") | |
| analyze_button.click( | |
| fn=classify_keywords, | |
| inputs=[urls_input, keywords_input], | |
| outputs=[df_output, file_output, summary_output] | |
| ) | |
| demo.launch() |