Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import numpy as np | |
| import os | |
| import joblib | |
| from sklearn.ensemble import RandomForestClassifier | |
| from sklearn.model_selection import train_test_split | |
| from sklearn.metrics import classification_report, accuracy_score | |
| from xgboost import XGBClassifier | |
| DATASET_PATHS = [ | |
| "../datasets/phishing.csv", | |
| "../datasets/phishing_site_urls.csv", | |
| "../datasets/dataset.csv", | |
| "../datasets/verified_online.csv", | |
| ] | |
| MODEL_DIR = "." | |
| FEATURE_COLS = [ | |
| "UsingIP", "LongURL", "ShortURL", "Symbol@", "Redirecting//", | |
| "PrefixSuffix-", "SubDomains", "HTTPS", "DomainRegLen", "Favicon", | |
| "NonStdPort", "HTTPSDomainURL", "RequestURL", "AnchorURL", | |
| "LinksInScriptTags", "ServerFormHandler", "InfoEmail", "AbnormalURL", | |
| "WebsiteForwarding", "StatusBarCust", "DisableRightClick", | |
| "UsingPopupWindow", "IframeRedirection", "AgeofDomain", "DNSRecording", | |
| "WebsiteTraffic", "PageRank", "GoogleIndex", "LinksPointingToPage", | |
| "StatsReport" | |
| ] | |
| def load_data(): | |
| print("[*] Looking for dataset...") | |
| df = None | |
| for path in DATASET_PATHS: | |
| if os.path.exists(path): | |
| df = pd.read_csv(path) | |
| print(f"[+] Loaded: {path}") | |
| break | |
| if df is None: | |
| print("[!] Dataset not found. Tried:") | |
| for p in DATASET_PATHS: | |
| print(f" {p}") | |
| return None, None, None | |
| print(f"[*] Shape: {df.shape}") | |
| print(f"[*] Columns: {list(df.columns)}") | |
| label_col = "class" if "class" in df.columns else None | |
| if label_col is None: | |
| for col in df.columns: | |
| if col.lower() in ["class", "label", "result", "type", "target"]: | |
| label_col = col | |
| break | |
| if label_col is None: | |
| print("[!] Could not find label column.") | |
| return None, None, None | |
| print(f"[*] Label column: '{label_col}'") | |
| print(f"[*] Class distribution:\n{df[label_col].value_counts()}") | |
| feature_cols = [c for c in FEATURE_COLS if c in df.columns] | |
| if len(feature_cols) < 5: | |
| feature_cols = [c for c in df.columns if c not in [label_col, "Index", "index"]] | |
| print(f"[*] Using {len(feature_cols)} feature columns") | |
| X = df[feature_cols].fillna(0) | |
| y = df[label_col].apply(lambda v: 1 if v == 1 else 0) | |
| print(f"[*] Final label distribution: {dict(y.value_counts())}") | |
| return X, y, feature_cols | |
| def train_models(X, y, feature_cols): | |
| X_train, X_test, y_train, y_test = train_test_split( | |
| X, y, test_size=0.2, random_state=42, stratify=y | |
| ) | |
| print(f"\n[*] Train: {len(X_train)} | Test: {len(X_test)}") | |
| print("\n[*] Training Random Forest...") | |
| rf = RandomForestClassifier( | |
| n_estimators=100, max_depth=20, | |
| min_samples_split=5, random_state=42, n_jobs=-1 | |
| ) | |
| rf.fit(X_train, y_train) | |
| rf_preds = rf.predict(X_test) | |
| rf_acc = accuracy_score(y_test, rf_preds) | |
| print(f"[+] Random Forest Accuracy: {rf_acc*100:.2f}%") | |
| print(classification_report(y_test, rf_preds, target_names=["Legitimate", "Phishing"])) | |
| print("[*] Training XGBoost...") | |
| xgb = XGBClassifier( | |
| n_estimators=100, max_depth=6, learning_rate=0.1, | |
| eval_metric="logloss", random_state=42, n_jobs=-1 | |
| ) | |
| xgb.fit(X_train, y_train) | |
| xgb_preds = xgb.predict(X_test) | |
| xgb_acc = accuracy_score(y_test, xgb_preds) | |
| print(f"[+] XGBoost Accuracy: {xgb_acc*100:.2f}%") | |
| print(classification_report(y_test, xgb_preds, target_names=["Legitimate", "Phishing"])) | |
| best = rf if rf_acc >= xgb_acc else xgb | |
| best_name = "RandomForest" if rf_acc >= xgb_acc else "XGBoost" | |
| best_acc = max(rf_acc, xgb_acc) | |
| print(f"\n[+] Best model: {best_name} ({best_acc*100:.2f}%)") | |
| os.makedirs(MODEL_DIR, exist_ok=True) | |
| # Use joblib consistently for all models to ensure compatibility | |
| joblib.dump(rf, os.path.join(MODEL_DIR, "url_rf_model.pkl"), compress=3) | |
| joblib.dump(xgb, os.path.join(MODEL_DIR, "url_xgb_model.pkl"), compress=3) | |
| joblib.dump(best, os.path.join(MODEL_DIR, "url_best_model.pkl"), compress=3) | |
| joblib.dump(feature_cols, os.path.join(MODEL_DIR, "url_feature_cols.pkl"), compress=3) | |
| with open(os.path.join(MODEL_DIR, "url_model_info.txt"), "w") as f: | |
| f.write(f"Best model: {best_name}\n") | |
| f.write(f"Accuracy: {best_acc*100:.2f}%\n") | |
| f.write(f"RF Accuracy: {rf_acc*100:.2f}%\n") | |
| f.write(f"XGB Accuracy: {xgb_acc*100:.2f}%\n") | |
| f.write(f"Features: {feature_cols}\n") | |
| f.write(f"Model type saved: {type(best).__name__}\n") | |
| print("[+] Saved: url_best_model.pkl, url_rf_model.pkl, url_xgb_model.pkl, url_feature_cols.pkl") | |
| # Verify that best is a proper sklearn model with required methods | |
| if not hasattr(best, 'predict') or not hasattr(best, 'predict_proba'): | |
| raise ValueError(f"ERROR: Saved model does not have required methods. Type: {type(best)}") | |
| importances = pd.Series(best.feature_importances_, index=feature_cols) | |
| top5 = importances.nlargest(5) | |
| print("\n[*] Top 5 most important features:") | |
| for feat, score in top5.items(): | |
| print(f" {feat}: {score:.4f}") | |
| if __name__ == "__main__": | |
| print("=" * 50) | |
| print(" URL Phishing Classifier - Training Script") | |
| print("=" * 50) | |
| X, y, feature_cols = load_data() | |
| if X is not None: | |
| train_models(X, y, feature_cols) | |
| print("\n[✓] Training complete!") | |
| else: | |
| print("\n[!] Training failed. Check dataset path.") |