diff --git a/MainController.py b/MainController.py index 768da89..76c0470 100644 --- a/MainController.py +++ b/MainController.py @@ -5,6 +5,7 @@ from models.NetworkingScanner import NetworkScanner from models.Sniffer import packet_callback, sniff from models.MusicPlayer import MusicPlayerModel from models.GamblingGameModel import GamblingGameModel +from models.ScraperModel import ScraperModel import tkinter as tk import time import datetime @@ -21,6 +22,9 @@ class MainController: self.game_model = GamblingGameModel() self.roulette_running = False self.roulette_thread = None + + self.model = ScraperModel() # Modelo para gestionar el scraping + self.scraper_running = False def connect_events(self): """Conecta los eventos de la vista con las funciones del controlador.""" @@ -37,6 +41,54 @@ class MainController: self.view.button_start_roulette.config(command=self.start_roulette) self.view.button_stop_roulette.config(command=self.stop_roulette) + """Conecta los eventos de la vista con las funciones del controlador.""" + self.view.button_start_scraper.config(command=self.start_scraper_thread) + self.view.button_stop_scraper.config(command=self.stop_scraper) + + + def start_scraper_thread(self): + """Inicia el scraping en un hilo separado.""" + start_url = self.view.entry_scraper_url.get().strip() + if not start_url: + self.view.text_scraper_output.insert("end", "Por favor, introduce una URL válida.\n") + return + + self.model.add_url(start_url) # Añadir URL inicial al modelo + self.scraper_running = True + self.view.button_start_scraper.config(state="disabled") + self.view.button_stop_scraper.config(state="normal") + + # Iniciar hilo para el scraping + threading.Thread(target=self.run_scraper, daemon=True).start() + + + def stop_scraper(self): + """Detiene el scraping.""" + self.scraper_running = False + self.view.button_start_scraper.config(state="normal") + self.view.button_stop_scraper.config(state="disabled") + self.view.text_scraper_output.insert("end", "Scraper detenido.\n") + + + def run_scraper(self): + """Ejecuta el scraping y actualiza la interfaz con los resultados.""" + while self.scraper_running and self.model.has_pending_urls(): + current_url, result = self.model.scrape_next_url() + + if isinstance(result, str): # Error + self.view.text_scraper_output.insert("end", f"{result}\n") + else: # Éxito + self.view.text_scraper_output.insert("end", f"Explorando: {current_url}\n") + for link in result: + self.view.text_scraper_output.insert("end", f" - {link}\n") + + self.view.text_scraper_output.see("end") + time.sleep(1) # Retardo para evitar sobrecarga + + self.scraper_running = False + self.view.text_scraper_output.insert("end", "Scraping completado.\n") + self.view.button_start_scraper.config(state="normal") + self.view.button_stop_scraper.config(state="disabled") def start_sniffer_thread(self): """Inicia el sniffer en un hilo separado.""" diff --git a/MainView.py b/MainView.py index 7e2c15f..708235c 100644 --- a/MainView.py +++ b/MainView.py @@ -38,6 +38,8 @@ class MainView: self.create_status_bar() self.create_music_player() + + self.create_scraper_ui() def create_tabs(self): """Crea las solapas dentro del notebook.""" @@ -189,3 +191,27 @@ class MainView: self.result_label = ttk.Label(tab, text="", font=("Arial", 12), foreground="blue") self.result_label.pack(pady=10) + + def create_scraper_ui(self): + """ + Crea la interfaz para el scraping: + - Input y botones en el frame_left. + - TextArea de resultados en la Solapa 5. + """ + # --- Input y botones en frame_left --- + tk.Label(self.frame_left, text="URL del Scraper:", bg="lightblue", font=("Arial", 10)).pack(pady=5) + self.entry_scraper_url = tk.Entry(self.frame_left, width=25, font=("Arial", 10)) + self.entry_scraper_url.pack(pady=5) + + self.button_start_scraper = tk.Button(self.frame_left, text="Iniciar Scraper", font=("Arial", 10)) + self.button_start_scraper.pack(pady=5) + + self.button_stop_scraper = tk.Button(self.frame_left, text="Detener Scraper", font=("Arial", 10), state="disabled") + self.button_stop_scraper.pack(pady=5) + + # --- TextArea en Solapa 5 --- + tab5 = self.notebook.nametowidget(self.notebook.tabs()[4]) # Obtener la solapa 5 + tk.Label(tab5, text="Resultados del Scraper", font=("Arial", 12)).pack(pady=5) + self.text_scraper_output = scrolledtext.ScrolledText(tab5, wrap=tk.WORD, height=20, width=80) + self.text_scraper_output.pack(pady=10, padx=10, fill="both", expand=True) + diff --git a/README.md b/README.md index cc3bd4b..43d9118 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,49 @@ El objetivo principal de este proyecto es implementar tareas distribuidas en dif --- +## 📝 Explicación de las principales funcionalidades + +### Solapas (Tabs) + +1. **IP Tracker** + - Permite rastrear información sobre una dirección IP, como ubicación, tipo, y más, utilizando la API de ipwhois.io. + - Los resultados se muestran en un área de texto con scroll. + +2. **Escaneo de Red** + - Realiza un escaneo de dispositivos conectados a la red local usando la biblioteca `python-nmap`. + - Lista dispositivos encontrados con sus respectivas direcciones IP y MAC. + +3. **Sniffer** + - Captura y analiza paquetes de red en tiempo real utilizando `Scapy`. + - Muestra información relevante del paquete, como origen, destino, protocolo y puertos. + - Incluye funcionalidad para iniciar y detener el análisis. + +4. **Juego de Azar (Ruleta)** + - Un mini-juego de ruleta donde el usuario puede apostar seleccionando un número del 1 al 10. + - Calcula si el jugador gana o pierde y actualiza el saldo en la interfaz. + +5. **Scraper** + - Permite explorar una URL inicial y extraer enlaces relacionados usando `BeautifulSoup`. + - Los resultados se almacenan en una base de datos MongoDB y se muestran en la interfaz en tiempo real. + +### Frame Izquierdo +- **Gestión del Scraper:** + - Entrada para la URL inicial del scraping. + - Botones para iniciar y detener la operación. + +### Frame Derecho +- **Reproductor de Música:** + - Lista canciones disponibles en una carpeta local. + - Botones para reproducir, detener y actualizar la lista de canciones. + +### Barra Inferior (Estado) +- **Indicadores del sistema:** + - Uso de CPU y RAM en tiempo real. + - Velocidad de subida y descarga de red. + - Fecha y hora actualizadas dinámicamente. + +--- + ## 🌍 Contribuciones Si deseas contribuir a este proyecto: @@ -90,12 +133,5 @@ Si deseas contribuir a este proyecto: ## 🎮 Autor -**Kevin Developer**\ -[GitHub](https://github.com/KevinOlarte1) - ---- - -## 📝 Tutorial - -(En este apartado podrás incluir un tutorial detallado para que los usuarios aprendan a utilizar tu proyecto paso a paso.) - +**Kevin Developer** +[GitHub](https://github.com/KevinOlarte1) diff --git a/__pycache__/MainController.cpython-313.pyc b/__pycache__/MainController.cpython-313.pyc index 9287fb3..3d9ddc5 100644 Binary files a/__pycache__/MainController.cpython-313.pyc and b/__pycache__/MainController.cpython-313.pyc differ diff --git a/__pycache__/MainView.cpython-313.pyc b/__pycache__/MainView.cpython-313.pyc index 2aaea59..9462290 100644 Binary files a/__pycache__/MainView.cpython-313.pyc and b/__pycache__/MainView.cpython-313.pyc differ diff --git a/models/ScraperModel.py b/models/ScraperModel.py new file mode 100644 index 0000000..1d41b94 --- /dev/null +++ b/models/ScraperModel.py @@ -0,0 +1,63 @@ +import requests +from bs4 import BeautifulSoup +from urllib.parse import urljoin +from pymongo import MongoClient + + +class ScraperModel: + def __init__(self): + self.to_visit = [] + self.visited = set() + + # Conexión a MongoDB + self.client = MongoClient("mongodb://localhost:27017/") + self.db = self.client["scraping"] + self.collection = self.db["visited_links"] + + # Crear índice único para evitar duplicados + self.collection.create_index("url", unique=True) + + def add_url(self, url): + """Añade una URL a la lista de pendientes.""" + if url not in self.visited and url not in self.to_visit: + self.to_visit.append(url) + + def scrape_next_url(self): + """Scrapea la siguiente URL.""" + if not self.to_visit: + return None, [] + + current_url = self.to_visit.pop(0) + self.visited.add(current_url) + + try: + # Solicitar la URL + response = requests.get(current_url, timeout=10) + response.raise_for_status() + except requests.RequestException as e: + return current_url, f"Error al acceder a {current_url}: {e}" + + # Procesar los enlaces encontrados + soup = BeautifulSoup(response.text, 'html.parser') + found_links = [] + + for link in soup.find_all('a', href=True): + full_url = urljoin(current_url, link['href']) + if full_url not in self.visited and full_url not in self.to_visit: + self.to_visit.append(full_url) + found_links.append(full_url) + + # Guardar URL visitada en MongoDB + self.save_to_database(current_url) + return current_url, found_links + + def save_to_database(self, url): + """Guarda la URL visitada en la base de datos.""" + try: + self.collection.insert_one({"url": url}) + except Exception as e: + print(f"Error al guardar en la base de datos: {e}") + + def has_pending_urls(self): + """Verifica si hay URLs pendientes.""" + return bool(self.to_visit) diff --git a/models/__pycache__/GamblingGameModel.cpython-313.pyc b/models/__pycache__/GamblingGameModel.cpython-313.pyc index ab30132..a990875 100644 Binary files a/models/__pycache__/GamblingGameModel.cpython-313.pyc and b/models/__pycache__/GamblingGameModel.cpython-313.pyc differ diff --git a/models/__pycache__/MusicPlayer.cpython-313.pyc b/models/__pycache__/MusicPlayer.cpython-313.pyc index 1ef4311..fff023b 100644 Binary files a/models/__pycache__/MusicPlayer.cpython-313.pyc and b/models/__pycache__/MusicPlayer.cpython-313.pyc differ diff --git a/models/__pycache__/NetworkingScanner.cpython-313.pyc b/models/__pycache__/NetworkingScanner.cpython-313.pyc index c4c4bc2..a8db9bb 100644 Binary files a/models/__pycache__/NetworkingScanner.cpython-313.pyc and b/models/__pycache__/NetworkingScanner.cpython-313.pyc differ diff --git a/models/__pycache__/ScraperModel.cpython-313.pyc b/models/__pycache__/ScraperModel.cpython-313.pyc new file mode 100644 index 0000000..3b6a2b6 Binary files /dev/null and b/models/__pycache__/ScraperModel.cpython-313.pyc differ diff --git a/models/__pycache__/Sniffer.cpython-313.pyc b/models/__pycache__/Sniffer.cpython-313.pyc index 29ec3a0..590701a 100644 Binary files a/models/__pycache__/Sniffer.cpython-313.pyc and b/models/__pycache__/Sniffer.cpython-313.pyc differ diff --git a/models/__pycache__/SystemStats.cpython-313.pyc b/models/__pycache__/SystemStats.cpython-313.pyc index abbc1fc..f098b60 100644 Binary files a/models/__pycache__/SystemStats.cpython-313.pyc and b/models/__pycache__/SystemStats.cpython-313.pyc differ diff --git a/pruebas.py b/pruebas.py index 8620488..a003b29 100644 --- a/pruebas.py +++ b/pruebas.py @@ -1,123 +1,150 @@ -import tkinter as tk -from tkinter import messagebox -import threading -import random +import requests +from bs4 import BeautifulSoup +from urllib.parse import urljoin +from pymongo import MongoClient import time +import tkinter as tk +from tkinter import scrolledtext +import threading -class GamblingGameWithThreads: +def setup_database(): + """ + Configura la conexión a la base de datos MongoDB y retorna la colección. + """ + client = MongoClient("mongodb://localhost:27017/") # Conectar a MongoDB + db = client["scraping"] # Base de datos llamada 'scraping' + collection = db["visited_links"] # Colección llamada 'visited_links' + + # Crear índice único para evitar duplicados + collection.create_index("url", unique=True) + + return collection + + +def insert_visited_link(collection, url): + """ + Inserta una URL visitada en la base de datos. + """ + try: + collection.insert_one({"url": url}) + except Exception as e: + print(f"Error al insertar la URL visitada {url}: {e}") + + +class ScraperApp: def __init__(self, root): self.root = root - self.root.title("Juego de Azar - Ruleta con Hilos") - self.root.geometry("400x400") + self.root.title("Scraper de Enlaces con Hilos") + self.root.geometry("800x500") - # Variables del juego - self.balance = 100 - self.roulette_number = None - self.roulette_running = False - self.roulette_thread = None + # Base de datos MongoDB + self.collection = setup_database() - # Etiqueta de saldo - self.balance_label = tk.Label(self.root, text=f"Saldo: $ {self.balance}", font=("Arial", 14)) - self.balance_label.pack(pady=10) + # Variable para detener el scraping + self.running = False - # Entrada para la apuesta - tk.Label(self.root, text="Tu Apuesta ($):", font=("Arial", 12)).pack() - self.bet_entry = tk.Entry(self.root, width=10, font=("Arial", 12)) - self.bet_entry.pack(pady=5) + # Frame para la URL y botón + frame_top = tk.Frame(self.root) + frame_top.pack(pady=10) - # Entrada para elegir número - tk.Label(self.root, text="Elige un número (1-10):", font=("Arial", 12)).pack() - self.number_entry = tk.Entry(self.root, width=10, font=("Arial", 12)) - self.number_entry.pack(pady=5) + tk.Label(frame_top, text="Introduce la URL inicial:", font=("Arial", 12)).pack(side=tk.LEFT, padx=5) + self.url_entry = tk.Entry(frame_top, width=50, font=("Arial", 12)) + self.url_entry.pack(side=tk.LEFT, padx=5) + self.start_button = tk.Button(frame_top, text="Iniciar Scraping", font=("Arial", 12), command=self.start_scraping) + self.start_button.pack(side=tk.LEFT, padx=5) - # Botones de control - self.start_button = tk.Button(self.root, text="Iniciar Ruleta", font=("Arial", 12), command=self.start_roulette) - self.start_button.pack(pady=10) + self.stop_button = tk.Button(frame_top, text="Detener Scraping", font=("Arial", 12), command=self.stop_scraping, state="disabled") + self.stop_button.pack(side=tk.LEFT, padx=5) - self.stop_button = tk.Button(self.root, text="Detener Ruleta", font=("Arial", 12), state="disabled", command=self.stop_roulette) - self.stop_button.pack(pady=5) + # TextArea para los resultados + self.result_area = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, font=("Arial", 12), height=20, width=90) + self.result_area.pack(pady=10) - # Resultado del juego - self.result_label = tk.Label(self.root, text="", font=("Arial", 12), fg="blue") - self.result_label.pack(pady=10) - - # Número de la ruleta en tiempo real - self.roulette_label = tk.Label(self.root, text="Ruleta: ---", font=("Arial", 16), fg="red") - self.roulette_label.pack(pady=10) - - def start_roulette(self): - """Inicia el giro de la ruleta en un hilo.""" - if self.roulette_running: - messagebox.showwarning("Advertencia", "La ruleta ya está girando.") + def start_scraping(self): + """ + Inicia el scraping en un hilo separado. + """ + start_url = self.url_entry.get().strip() + if not start_url: + self.result_area.insert(tk.END, "Por favor, introduce una URL válida.\n") return - try: - bet = int(self.bet_entry.get()) - chosen_number = int(self.number_entry.get()) - except ValueError: - messagebox.showerror("Error", "Por favor, ingresa valores numéricos válidos.") - return + self.result_area.insert(tk.END, f"Iniciando scraping desde: {start_url}\n") + self.result_area.see(tk.END) - if bet <= 0 or chosen_number < 1 or chosen_number > 10: - messagebox.showwarning("Advertencia", "La apuesta debe ser mayor a $0 y elige un número entre 1 y 10.") - return - - if bet > self.balance: - messagebox.showwarning("Advertencia", "No tienes suficiente saldo para esta apuesta.") - return - - self.bet = bet - self.chosen_number = chosen_number - self.roulette_running = True + self.running = True self.start_button.config(state="disabled") self.stop_button.config(state="normal") - # Crear y arrancar el hilo de la ruleta - self.roulette_thread = threading.Thread(target=self.spin_roulette) - self.roulette_thread.start() + # Iniciar un hilo para el scraping + self.scraping_thread = threading.Thread(target=self.scrape_links_forever, args=(start_url,)) + self.scraping_thread.daemon = True # Hilo se detiene cuando la app se cierra + self.scraping_thread.start() - def spin_roulette(self): - """Simula el giro continuo de la ruleta.""" - while self.roulette_running: - self.roulette_number = random.randint(1, 10) - self.roulette_label.config(text=f"Ruleta: {self.roulette_number}") - time.sleep(0.1) - - def stop_roulette(self): - """Detiene la ruleta y evalúa el resultado del juego.""" - if not self.roulette_running: - return - - self.roulette_running = False + def stop_scraping(self): + """ + Detiene el scraping. + """ + self.running = False + self.result_area.insert(tk.END, "Deteniendo scraping...\n") + self.result_area.see(tk.END) self.start_button.config(state="normal") self.stop_button.config(state="disabled") - # Evaluar resultado - if self.chosen_number == self.roulette_number: - winnings = self.bet * 2 - self.balance += winnings - self.result_label.config( - text=f"¡Ganaste! El número fue {self.roulette_number}. Ganaste $ {winnings}.", - fg="green", - ) - else: - self.balance -= self.bet - self.result_label.config( - text=f"Perdiste. El número fue {self.roulette_number}. Perdiste $ {self.bet}.", - fg="red", - ) + def scrape_links_forever(self, start_url): + """ + Scrapea enlaces indefinidamente y guarda solo los visitados en MongoDB. + Se ejecuta en un hilo separado. + + Args: + start_url (str): URL inicial para comenzar el scraping. + """ + to_visit = [start_url] # Lista de URLs por visitar + visited = set() # Conjunto para evitar bucles - # Actualizar saldo - self.balance_label.config(text=f"Saldo: $ {self.balance}") + while to_visit and self.running: + current_url = to_visit.pop(0) # Tomar la URL actual de la lista - # Revisar si el jugador se quedó sin saldo - if self.balance <= 0: - messagebox.showinfo("Juego Terminado", "¡Te quedaste sin saldo! Gracias por jugar.") - + if current_url in visited: + continue + + self.result_area.insert(tk.END, f"Explorando: {current_url}\n") + self.result_area.see(tk.END) + visited.add(current_url) # Marcar como visitada + + try: + # Realizar la solicitud HTTP + response = requests.get(current_url, timeout=10) + response.raise_for_status() + except requests.RequestException as e: + self.result_area.insert(tk.END, f"Error al acceder a {current_url}: {e}\n") + self.result_area.see(tk.END) + continue + + # Parsear el contenido HTML + soup = BeautifulSoup(response.text, 'html.parser') + + # Encontrar y procesar los enlaces + for link in soup.find_all('a', href=True): + full_url = urljoin(current_url, link['href']) + if full_url not in visited and full_url not in to_visit: + to_visit.append(full_url) + + # Insertar la URL visitada en la base de datos + insert_visited_link(self.collection, current_url) + + # Retardo para evitar sobrecargar el servidor + time.sleep(1) + + # Finalización del scraping + self.result_area.insert(tk.END, "Scraping finalizado.\n") + self.result_area.see(tk.END) + self.start_button.config(state="normal") + self.stop_button.config(state="disabled") if __name__ == "__main__": root = tk.Tk() - app = GamblingGameWithThreads(root) + app = ScraperApp(root) root.mainloop()