Files
AccountForger-neuerUpload/utils/proxy_rotator.py
Claude Project Manager 04585e95b6 Initial commit
2025-08-01 23:50:28 +02:00

413 Zeilen
14 KiB
Python

# Path: p:/Chimaira/Code-Playwright/utils/proxy_rotator.py
"""
Proxy-Rotations- und Verwaltungsfunktionalität.
"""
import os
import json
import random
import logging
import requests
import time
from typing import Dict, List, Any, Optional, Tuple, Union
logger = logging.getLogger("proxy_rotator")
class ProxyRotator:
"""Klasse zur Verwaltung und Rotation von Proxies."""
CONFIG_FILE = os.path.join("config", "proxies.json")
def __init__(self):
"""Initialisiert den ProxyRotator und lädt die Konfiguration."""
self.config = self.load_config()
self.current_proxy = None
self.last_rotation_time = 0
# Stelle sicher, dass das Konfigurationsverzeichnis existiert
os.makedirs(os.path.dirname(self.CONFIG_FILE), exist_ok=True)
def load_config(self) -> Dict[str, Any]:
"""Lädt die Proxy-Konfiguration aus der Konfigurationsdatei."""
if not os.path.exists(self.CONFIG_FILE):
return {
"ipv4": [],
"ipv6": [],
"mobile": [],
"mobile_api": {
"marsproxies": "",
"iproyal": ""
},
"rotation_interval": 300 # 5 Minuten
}
try:
with open(self.CONFIG_FILE, "r", encoding="utf-8") as f:
config = json.load(f)
logger.info(f"Proxy-Konfiguration geladen: {len(config.get('ipv4', []))} IPv4, "
f"{len(config.get('ipv6', []))} IPv6, {len(config.get('mobile', []))} Mobile")
return config
except Exception as e:
logger.error(f"Fehler beim Laden der Proxy-Konfiguration: {e}")
return {
"ipv4": [],
"ipv6": [],
"mobile": [],
"mobile_api": {
"marsproxies": "",
"iproyal": ""
},
"rotation_interval": 300
}
def save_config(self) -> bool:
"""Speichert die Proxy-Konfiguration in die Konfigurationsdatei."""
try:
with open(self.CONFIG_FILE, "w", encoding="utf-8") as f:
json.dump(self.config, f, indent=2)
logger.info("Proxy-Konfiguration gespeichert")
return True
except Exception as e:
logger.error(f"Fehler beim Speichern der Proxy-Konfiguration: {e}")
return False
def get_config(self) -> Dict[str, Any]:
"""Gibt die aktuelle Konfiguration zurück."""
return self.config
def update_config(self, new_config: Dict[str, Any]) -> bool:
"""Aktualisiert die Konfiguration mit den neuen Werten."""
try:
# Aktualisiere nur die bereitgestellten Schlüssel
for key, value in new_config.items():
self.config[key] = value
# Konfiguration speichern
return self.save_config()
except Exception as e:
logger.error(f"Fehler beim Aktualisieren der Proxy-Konfiguration: {e}")
return False
def get_proxies_by_type(self, proxy_type: str) -> List[str]:
"""Gibt eine Liste von Proxies des angegebenen Typs zurück."""
if proxy_type.lower() not in ["ipv4", "ipv6", "mobile"]:
logger.warning(f"Ungültiger Proxy-Typ: {proxy_type}")
return []
return self.config.get(proxy_type.lower(), [])
def get_random_proxy(self, proxy_type: str) -> Optional[str]:
"""Gibt einen zufälligen Proxy des angegebenen Typs zurück."""
proxies = self.get_proxies_by_type(proxy_type)
if not proxies:
logger.warning(f"Keine Proxies vom Typ '{proxy_type}' verfügbar")
return None
return random.choice(proxies)
def get_proxy(self, proxy_type=None):
"""
Gibt eine Proxy-Konfiguration für den angegebenen Typ zurück.
Args:
proxy_type: Proxy-Typ ("ipv4", "ipv6", "mobile") oder None für zufälligen Typ
Returns:
Dict mit Proxy-Konfiguration oder None, wenn kein Proxy verfügbar ist
"""
try:
# Wenn kein Proxy-Typ angegeben ist, einen zufälligen verwenden
if proxy_type is None:
available_types = []
if self.config.get("ipv4"):
available_types.append("ipv4")
if self.config.get("ipv6"):
available_types.append("ipv6")
if self.config.get("mobile"):
available_types.append("mobile")
if not available_types:
logger.warning("Keine Proxies verfügbar")
return None
proxy_type = random.choice(available_types)
# Proxy vom angegebenen Typ holen
proxy_list = self.get_proxies_by_type(proxy_type)
if not proxy_list:
logger.warning(f"Keine Proxies vom Typ '{proxy_type}' verfügbar")
return None
# Zufälligen Proxy aus der Liste auswählen
proxy = random.choice(proxy_list)
# Proxy-URL parsen
parts = proxy.split(":")
if len(parts) >= 4:
# Format: host:port:username:password
host, port, username, password = parts[:4]
return {
"server": f"http://{host}:{port}",
"username": username,
"password": password
}
elif len(parts) >= 2:
# Format: host:port
host, port = parts[:2]
return {
"server": f"http://{host}:{port}"
}
else:
logger.warning(f"Ungültiges Proxy-Format: {self.mask_proxy_credentials(proxy)}")
return None
except Exception as e:
logger.error(f"Fehler beim Abrufen des Proxys: {e}")
return None
def get_next_proxy(self, proxy_type: str, force_rotation: bool = False) -> Optional[str]:
"""
Gibt den nächsten zu verwendenden Proxy zurück, unter Berücksichtigung des Rotationsintervalls.
Args:
proxy_type: Typ des Proxys (ipv4, ipv6, mobile)
force_rotation: Erzwingt eine Rotation, unabhängig vom Zeitintervall
Returns:
Proxy-String oder None, wenn kein Proxy verfügbar ist
"""
current_time = time.time()
interval = self.config.get("rotation_interval", 300) # Standardintervall: 5 Minuten
# Rotation durchführen, wenn das Intervall abgelaufen ist oder erzwungen wird
if force_rotation or self.current_proxy is None or (current_time - self.last_rotation_time) > interval:
self.current_proxy = self.get_random_proxy(proxy_type)
self.last_rotation_time = current_time
if self.current_proxy:
logger.info(f"Proxy rotiert zu: {self.mask_proxy_credentials(self.current_proxy)}")
return self.current_proxy
def test_proxy(self, proxy_type: str) -> Dict[str, Any]:
"""
Testet einen Proxy des angegebenen Typs.
Args:
proxy_type: Typ des zu testenden Proxys
Returns:
Dictionary mit Testergebnissen
"""
proxy = self.get_random_proxy(proxy_type)
if not proxy:
return {
"success": False,
"error": f"Keine Proxies vom Typ '{proxy_type}' verfügbar"
}
try:
# Proxy-URL parsen
parts = proxy.split(":")
if len(parts) >= 4:
# Format: host:port:username:password
host, port, username, password = parts[:4]
proxy_url = f"http://{username}:{password}@{host}:{port}"
elif len(parts) >= 2:
# Format: host:port
host, port = parts[:2]
proxy_url = f"http://{host}:{port}"
else:
return {
"success": False,
"error": f"Ungültiges Proxy-Format: {self.mask_proxy_credentials(proxy)}"
}
# Proxy-Konfiguration für requests
proxies = {
"http": proxy_url,
"https": proxy_url
}
# Startzeit für Antwortzeit-Messung
start_time = time.time()
# Test-Anfrage über den Proxy
response = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=10)
# Antwortzeit berechnen
response_time = time.time() - start_time
if response.status_code == 200:
data = response.json()
ip = data.get("ip", "Unbekannt")
# Länderinformationen abrufen (optional)
country = self.get_country_for_ip(ip)
return {
"success": True,
"ip": ip,
"country": country,
"response_time": response_time,
"proxy_type": proxy_type
}
else:
return {
"success": False,
"error": f"Ungültige Antwort: HTTP {response.status_code}"
}
except requests.exceptions.Timeout:
return {
"success": False,
"error": "Zeitüberschreitung bei der Verbindung"
}
except requests.exceptions.ProxyError:
return {
"success": False,
"error": "Proxy-Fehler: Verbindung abgelehnt oder fehlgeschlagen"
}
except Exception as e:
logger.error(f"Fehler beim Testen des Proxys: {e}")
return {
"success": False,
"error": str(e)
}
def get_country_for_ip(self, ip: str) -> Optional[str]:
"""
Ermittelt das Land für eine IP-Adresse.
Args:
ip: IP-Adresse
Returns:
Ländername oder None im Fehlerfall
"""
try:
response = requests.get(f"https://ipapi.co/{ip}/json/", timeout=5)
if response.status_code == 200:
data = response.json()
return data.get("country_name")
return None
except Exception:
return None
def mask_proxy_credentials(self, proxy: str) -> str:
"""
Maskiert die Anmeldeinformationen in einem Proxy-String für die Protokollierung.
Args:
proxy: Original-Proxy-String
Returns:
Maskierter Proxy-String
"""
parts = proxy.split(":")
if len(parts) >= 4:
# Format: host:port:username:password
host, port = parts[0], parts[1]
return f"{host}:{port}:***:***"
return proxy
def add_proxy(self, proxy: str, proxy_type: str) -> bool:
"""
Fügt einen neuen Proxy zur Konfiguration hinzu.
Args:
proxy: Proxy-String im Format host:port:username:password
proxy_type: Typ des Proxys (ipv4, ipv6, mobile)
Returns:
True bei Erfolg, False bei Fehler
"""
if proxy_type.lower() not in ["ipv4", "ipv6", "mobile"]:
logger.warning(f"Ungültiger Proxy-Typ: {proxy_type}")
return False
proxy_list = self.config.get(proxy_type.lower(), [])
if proxy not in proxy_list:
proxy_list.append(proxy)
self.config[proxy_type.lower()] = proxy_list
self.save_config()
logger.info(f"Proxy hinzugefügt: {self.mask_proxy_credentials(proxy)} (Typ: {proxy_type})")
return True
return False
def remove_proxy(self, proxy: str, proxy_type: str) -> bool:
"""
Entfernt einen Proxy aus der Konfiguration.
Args:
proxy: Proxy-String
proxy_type: Typ des Proxys (ipv4, ipv6, mobile)
Returns:
True bei Erfolg, False bei Fehler
"""
if proxy_type.lower() not in ["ipv4", "ipv6", "mobile"]:
logger.warning(f"Ungültiger Proxy-Typ: {proxy_type}")
return False
proxy_list = self.config.get(proxy_type.lower(), [])
if proxy in proxy_list:
proxy_list.remove(proxy)
self.config[proxy_type.lower()] = proxy_list
self.save_config()
logger.info(f"Proxy entfernt: {self.mask_proxy_credentials(proxy)} (Typ: {proxy_type})")
return True
return False
def format_proxy_for_playwright(self, proxy: str) -> Dict[str, str]:
"""
Formatiert einen Proxy-String für die Verwendung mit Playwright.
Args:
proxy: Proxy-String im Format host:port:username:password
Returns:
Dictionary mit Playwright-Proxy-Konfiguration
"""
parts = proxy.split(":")
if len(parts) >= 4:
# Format: host:port:username:password
host, port, username, password = parts[:4]
return {
"server": f"{host}:{port}",
"username": username,
"password": password
}
elif len(parts) >= 2:
# Format: host:port
host, port = parts[:2]
return {
"server": f"{host}:{port}"
}
else:
logger.warning(f"Ungültiges Proxy-Format: {self.mask_proxy_credentials(proxy)}")
return {}