NEWS
IP-Scanner für das Hausnetz.
-
Das ist ein kleiner Webserver-Scanner für das eigene Netzwerk. Das Programm listet dann die gefundenen Adressen auf und durch einen Mausklick kann die entsprechende Adresse direkt aufgerufen werden.
Der IP-Bereich kann eingestellt werden.
Ich habe mal versucht eine Anleitung für das Programm zu schreiben.
-
Entpacke die ZIP-Datei.
-
Installiere PyInstaller (falls noch nicht geschehen):
pip install pyinstaller
-
Starte das Build-Skript build_webscanner.bat per Doppelklick oder über die Eingabeaufforderung:
build_webscanner.bat
-
Die fertige EXE-Datei findest du danach im Ordner dist.
Hier der Inhalt der einzelnen Dateien. Nichts böses drin.
import tkinter as tk from tkinter import ttk import webbrowser import socket import threading import queue from concurrent.futures import ThreadPoolExecutor START_IP = "192.168.49.1" END_IP = "192.168.49.254" PORTS = [80] def ip_to_int(ip): parts = list(map(int, ip.split("."))) return (parts[0]<<24) + (parts[1]<<16) + (parts[2]<<8) + parts[3] def int_to_ip(i): return f"{(i>>24)&0xFF}.{(i>>16)&0xFF}.{(i>>8)&0xFF}.{i&0xFF}" def generate_ips(start_ip, end_ip): start = ip_to_int(start_ip) end = ip_to_int(end_ip) for i in range(start, end+1): yield int_to_ip(i) class WebserverScanner: def __init__(self, progress_callback=None): self.progress_callback = progress_callback def scan_ip_port(self, ip, port): try: with socket.create_connection((ip, port), timeout=0.5): return True except: return False def get_hostname(self, ip): try: return socket.gethostbyaddr(ip)[0] except: return "Kein Hostname" def scan_threaded(self, ips, ports): results = [] total = len(ips)*len(ports) count_lock = threading.Lock() count = [0] def scan_one(ip_port): ip, port = ip_port if self.scan_ip_port(ip, port): hostname = self.get_hostname(ip) res = {"ip": ip, "hostname": hostname, "port": port} else: res = None with count_lock: count[0] += 1 if self.progress_callback: self.progress_callback(count[0], total) return res ip_ports = [(ip, port) for ip in ips for port in ports] with ThreadPoolExecutor(max_workers=100) as executor: for r in executor.map(scan_one, ip_ports): if r: results.append(r) return results class WebserverViewerApp: def __init__(self, root): self.root = root self.root.title("Webserver Scanner 192.168.49.x") self.root.geometry("900x700") self.entries = [] self.queue = queue.Queue() self.resize_after_id = None self.current_columns = None # Merke letzte Spaltenanzahl self.entries_displayed = None self.header = tk.Label(root, text="Webserver Scanner 192.168.49.1 - 254", font=("Arial", 18)) self.header.pack(pady=10) self.progress = ttk.Progressbar(root, orient="horizontal", length=600, mode="determinate") self.progress.pack(pady=10) self.status_label = tk.Label(root, text="Bereit", font=("Arial", 12)) self.status_label.pack() self.update_btn = tk.Button(root, text="Jetzt scannen", command=self.start_scan) self.update_btn.pack(pady=10) self.canvas = tk.Canvas(root) self.scroll_frame = tk.Frame(self.canvas) self.scrollbar = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview) self.canvas.configure(yscrollcommand=self.scrollbar.set) self.scrollbar.pack(side="right", fill="y") self.canvas.pack(fill="both", expand=True) # Erstelle Fenster-Item für scroll_frame im Canvas self.canvas_frame = self.canvas.create_window((0,0), window=self.scroll_frame, anchor="nw") # Scrollregion aktualisieren bei Größe des scroll_frame self.scroll_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) # Breite des scroll_frame immer an Canvas-Breite anpassen self.canvas.bind('<Configure>', self.resize_scroll_frame) # Debounced Resize-Handler für Fenstergröße self.root.bind("<Configure>", self.on_resize) self.root.after(100, self.check_queue) def resize_scroll_frame(self, event): self.canvas.itemconfig(self.canvas_frame, width=event.width) def on_resize(self, event): # Debounce mit 300ms Delay if self.resize_after_id: self.root.after_cancel(self.resize_after_id) self.resize_after_id = self.root.after(300, lambda: self.update_display(self.entries)) def start_scan(self): self.update_btn.config(state="disabled") self.progress['value'] = 0 self.status_label.config(text="Scan läuft...") threading.Thread(target=self.run_scan, daemon=True).start() def progress_callback(self, count, total): percent = int((count / total) * 100) self.queue.put(("progress", percent)) def run_scan(self): scanner = WebserverScanner(progress_callback=self.progress_callback) ips = list(generate_ips(START_IP, END_IP)) results = scanner.scan_threaded(ips, PORTS) self.queue.put(("done", results)) def check_queue(self): try: while True: msg, data = self.queue.get_nowait() if msg == "progress": self.progress['value'] = data self.status_label.config(text=f"Scan läuft... {data}%") elif msg == "done": self.entries = data self.update_display(data) self.status_label.config(text=f"Scan abgeschlossen. {len(data)} Server gefunden.") self.update_btn.config(state="normal") except queue.Empty: pass self.root.after(100, self.check_queue) def update_display(self, entries): # Berechne aktuelle Spaltenanzahl frame_width = self.canvas.winfo_width() or 900 box_min_width = 170 columns = max(1, frame_width // box_min_width) # Prüfe, ob Spaltenanzahl oder Einträge sich geändert haben if self.current_columns == columns and self.entries_displayed == entries: return self.current_columns = columns self.entries_displayed = entries # Alte Widgets löschen for widget in self.scroll_frame.winfo_children(): widget.destroy() if not entries: tk.Label(self.scroll_frame, text="Keine Webserver gefunden", font=("Arial", 14), fg="red").pack(pady=20) return box_height = 110 for index, entry in enumerate(entries): row = index // columns col = index % columns container = tk.Frame(self.scroll_frame, bd=1, relief="solid", padx=5, pady=5) container.grid(row=row, column=col, padx=5, pady=5, sticky="nsew") ip_label = tk.Label(container, text=entry['ip'], font=("Arial", 12, "bold")) ip_label.pack() if entry['hostname']: host_label = tk.Label(container, text=entry['hostname'], font=("Arial", 10), fg="gray") host_label.pack() btn_color = "#4CAF50" if entry['port'] == 80 else "#2196F3" open_button = tk.Button(container, text=f"Öffnen (Port {entry['port']})", bg=btn_color, fg="white", command=lambda ip=entry['ip'], port=entry['port']: self.open_url(ip, port)) open_button.pack(pady=5, fill="x") # Spalten gleichmäßig verteilen for c in range(columns): self.scroll_frame.grid_columnconfigure(c, weight=1) def open_url(self, ip, port): url = f"https://{ip}" if port == 443 else f"http://{ip}" webbrowser.open(url) if __name__ == "__main__": root = tk.Tk() app = WebserverViewerApp(root) root.mainloop()
build_webscanner.bat
@echo off echo. echo =============================== echo Building webscanner.exe ... echo =============================== echo. pyinstaller webscanner.spec echo. echo Fertig! EXE liegt in /dist/webscanner.exe pause
webscanner.spec
# webscanner.spec block_cipher = None a = Analysis( ['webscanner.py'], pathex=[], binaries=[], datas=[], hiddenimports=[], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False, ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE( pyz, a.scripts, [], exclude_binaries=True, name='webscanner', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, console=False, # GUI-Anwendung (kein Terminalfenster) ) coll = COLLECT( exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='webscanner', )
Das Programm kann natürlich auch direkt im cmd-Fenster (Windows) durch python webscanner.py im entsprechenden Verzeichnis gestartet werden.
Vor dem Start oder der Erstellung der EXE-Datei muß natürlich der IP-Bereich in webscanner.py angepasst werden.
START_IP = "192.168.49.1" END_IP = "192.168.49.254" PORTS = [80]
Wenn es läuft, sollte es etwa so aussehen:
Viel Spaß damit
-
-
macht der das nicht auch ..
-
@arteck
Mag sein. Mein kleines PRogramm läuft "auserhalb" von ioBroker.Und das tolle an meinem Programm ist, es ist vollkommen kostenlos. Es kostet keine 5,95 €.
-
@beowolf sagte in IP-Scanner für das Hausnetz.:
Mein kleines PRogramm läuft "auserhalb" von ioBroker.
Deswegen hab ich es auch nach offTopic verschoben!
-
Ok. Danke.
-
Danke für das Skript.
Ich verwende dafür den static site generator pelican in Verbindung mit einem nginx Webserver.
Für jedes Gerät was ich ansteuerbar haben möchte lege ich eine kleine Datei an mit tite, subtext und Kategorie an. Vorteil ist, da nicht jedes Gerät auf Port 80 eine Admin Oberfläche anbietet (Cups,portainer, iobroker ) das individuell zu bearbeiten
Auf Basis eines templates wird dann meine link Webseite Life aktualisiert (pelican läuft als docker Container und überwacht ob sich in einem Verzeichnis was ändert und startet dann die Erzeugung neu.Den Webserver habe ich intern auf die Domain links gemapt. Meist reicht auch nur l oder li im Browser aus um die Seite aufzurufen
-
Ich verwende in der Regel Angry IP Scanner wenn ich ein eigenständiges Programm brauche.