基于传统机器学习的恶意网站检测:从特征工程到多模型对比完整流程
发布时间:2026/10/3 2:59:46 锦皓数字建站

简介基于传统机器学习的恶意网站检测项目适合计算机、人工智能、电子信息等专业的在校学生和毕业设计者使用。资源围绕黑名单与白名单网站数据提供从特征提取、可视化到模型训练与预测的完整流程通过SVM、随机森林、DNN等算法实现恶意网站自动识别可作为课程设计或毕设的实践参考。压缩包共7个文件包括5个Python脚本、1个数据压缩包和1个说明文档整体大小3.31MB脚本分别承担网站特征提取、类别分布可视化和机器学习建模等任务同时附带整理好的黑白名单整合数据便于直接运行复现。已有186人学习浏览项目代码经过运行验证结果稳定适合在现有基础上扩展功能或开展进一步实验也可根据自身数据做迁移尝试。1. 基于传统机器学习的恶意网站检测一份能跑通的毕设级完整流程做安全方向的人都知道现在恶意网站检测的主流思路是深度学习和流量分析但传统机器学习这条路一直没死——它训练快、可解释性强、特征可控特别适合做课程设计和毕设的完整闭环演示。这个项目把整个流程串得很完整数据准备、特征提取、可视化探索、三种模型训练和预测全都有而且代码是测试通过的不是那种下载下来还要自己补一堆依赖的半成品。适合正在做机器学习课程设计、安全方向毕设的同学或者想快速了解传统ML在安全领域怎么落地的从业者。接下来我从数据怎么组织开始把每条脚本的用途、参数含义和踩坑点都过一遍。2. 数据和特征提取恶意网站检测的原料从哪来2.1 黑白名单数据的组织方式和文件说明这个项目的数据包是data.zip里面存的是一批已经标注好的网址数据。所谓白名单就是正常网站黑名单就是被判定为恶意的网站两者放在一起构成监督学习的训练语料。解开data.zip之后你会在里面找到2.xlsx这个文件是白名单和黑名单特征整合后的最终产物也就是说数据已经帮你做完了最脏的那一步——标签对齐。我解压之后看了一下结构文件里每一行是一条URL除了URL本身之外还带标签列和若干特征列。标签列通常用0和1表示0代表白名单1代表黑名单。如果你自己想做扩展也可以从其他地方拿恶意URL数据源比如PhishTank的公开CSV但要注意字段映射这个项目的特征列顺序是固定的直接接外部数据容易错位。这里要提醒一个关键点不要直接拿原始URL去训练模型。机器学习模型吃的是数值特征不是字符串。URL里能提取的东西很多比如URL长度、是否包含IP地址、点的数量、敏感词出现的次数、路径深度等这些才是模型真正学习的信号。2.2 translate.py从原始URL到数值特征的转换逻辑核心的转换逻辑在translate.py里。这个脚本的作用是把原始URL变成一行行的数值特征向量输出的内容会被后续的SVM、随机森林和DNN直接使用。常见的做法是先把URL解析成协议、域名、路径几个部分然后分别提取统计特征和词法特征。# translate.py 核心逻辑示例 import pandas as pd from urllib.parse import urlparse def extract_features(url): parsed urlparse(url) features {} # 基础统计特征 features[url_length] len(url) features[num_dots] url.count(.) features[num_digits] sum(c.isdigit() for c in url) features[path_depth] len(parsed.path.split(/)) - 1 features[has_ip] 1 if parsed.netloc and parsed.netloc[-1].isdigit() else 0 # 敏感词特征 suspicious_words [login, verify, account, signin, update] features[suspicious_count] sum(1 for w in suspicious_words if w in url.lower()) return features这段代码的核心思路是把URL拆成结构化对象然后从里面抽取数值特征。url_length、num_dots这类统计特征反映URL的基本形态has_ip判断域名是不是纯IP地址——这一点对恶意网站检测很有用因为很多钓鱼站点直接使用IP地址搭建。suspicious_count是词法特征的典型例子恶意URL经常会伪装成登录页或账户验证页面这类词出现频率会明显偏高。参数层面的注意事项是特征维度的选择决定了模型的上限。特征太少模型学不到区分信息特征太多但相关性强又会引入噪声。这个项目的默认特征集大概在十几维左右对传统的SVM来说正好合适不需要做特别复杂的降维处理。2.3 特征文件格式与标签对齐训练前必须确认的三件事跑translate.py之前先确认三件事文件路径对不对、编码对不对、标签列是不是数值类型。我在复现时遇到过xlsx文件里标签列是字符串0和1的情况直接喂给sklearn会报数据格式错误。解决办法是加载数据后强制转成int# 标签列转数值类型 data[label] data[label].astype(int) print(样本总量:, len(data)) print(恶意样本占比:, data[label].mean().round(4))特征向量和标签对齐以后打印一下恶意样本占比。如果黑名单样本太少模型会偏向预测白名单也就是常说的类别不平衡问题。常见做法是用SMOTE过采样或者调整class_weight参数这个项目里如果数据比例失衡建议在模型训练时把class_weight设为balanced。3. 可视化探索typelist.py 到底在做什么3.1 特征分布的直观观测训练前必须看的数据形态typelist.py做的事情是把特征数据的分布可视化出来。虽然叫typelist但实际作用是帮你快速判断特征对标签的区分度。比如你画出URL长度这个特征在黑白名单两组样本上的分布如果两组数据重叠度很高说明这个特征区分力弱反之如果差异明显说明是有效特征。这个脚本依赖matplotlib和seaborn如果你在跑的时候报错没有这两个库直接pip install。可视化结果一般输出成直方图或箱线图观察维度可以看特征在不同标签下的均值差异、方差差异、是否存在明显离群点。离群点会干扰SVM的间隔计算所以这一步看似只是看看图实际上是为后面的模型选特征做铺垫。3.2 从可视化结果反推特征工程哪些特征值得保留实际看分布图的时候我一般会重点关注三个特征URL长度、路径深度、敏感词计数。恶意URL因为要携带钓鱼参数长度往往比正常URL长路径深度方面正常站点的URL路径层级相对固定恶意站点的路径经常嵌套多层敏感词相关特征直接对应攻击意图区分度通常最明显。如果看完可视化发现某几个特征的分布几乎完全一致那就可以在translate.py里把无关特征删掉。这一步不是必做的因为SVM在高维空间本身具备一定的特征筛选能力但当特征数量很少时删除无关特征能明显提升训练速度也能减小过拟合风险。4. 三套模型的实现与对比SVM、随机森林和DNN4.1 SVM.py线性与RBF核的选择问题SVM是这个项目里最经典也最稳的模型。恶意网站检测场景下特征维度不高、样本量不大SVM的收敛速度和泛化能力都很合适。项目里的SVM.py做了模型训练和预测的完整流程核心代码我拆开看是这样的# SVM.py 核心逻辑示例 from sklearn.svm import SVC from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report X data.drop(label, axis1).values y data[label].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model SVC(kernelrbf, C1.0, gammascale) model.fit(X_train, y_train) y_pred model.predict(X_test) print(classification_report(y_test, y_pred))这里kernelrbf是默认的RBF核适合处理非线性边界C是惩罚系数C越大对误分类的惩罚越重模型越复杂容易过拟合gamma控制RBF核的宽度gamma值越大决策边界越复杂。我在这个项目上的经验是C设1.0、gamma设scale对几十到几百维的特征来说属于安全区间不需要一上来就调参。不少人在第一步就踩坑把特征数据直接丢给SVM而不做标准化。RBF核基于距离计算如果URL长度这个特征的取值范围是0到2000而敏感词计数只有0到5量纲差异会直接压掉弱势特征的作用。虽然特征维度低我仍然会建议在训练前加一步标准化习惯用StandardScaler原因后面避坑章节详细说。4.2 forest_split.py随机森林的分裂过程与默认参数表现随机森林的代码思路是把决策树做集成天然对特征量纲不敏感所以如果你不想做标准化森林是比SVM更省心的选择。forest_split.py名字里的split指的就是树的节点分裂过程每次分裂都在随机选取的特征子集里找最优点。# forest_split.py 核心逻辑示例 from sklearn.ensemble import RandomForestClassifier model RandomForestClassifier( n_estimators100, max_depth10, min_samples_leaf2, random_state42 ) model.fit(X_train, y_train) importances model.feature_importances_ for name, imp in zip(feature_names, importances): print(f{name}: {imp:.4f})n_estimators代表决策树的数量数量越多模型越稳定但训练时间也会更长max_depth限制树的深度防止单棵树记住噪声min_samples_leaf限制叶子节点最少样本数设得越大模型越粗糙但泛化越好。feature_importances_是随机森林的一个额外福利可以告诉你哪些特征最重要。基于恶意网站数据的经验随机森林在这个场景下通常比SVM更好调因为它能处理特征之间的非线性交互。比如URL长度这个特征单独看区分力一般但当它和has_ip同时出现时对恶意网站的识别就非常准——这种组合效应SVM很难主动捕捉而随机森林在分裂时会自动找到这种组合。4.3 DNN.py用分类思想做恶意网站预测DNN.py是项目里唯一用到深度学习的脚本但它的本质仍然是分类而不是序列建模。也就是说我们不会把URL当成文本序列去跑LSTM而是把translate.py提取的特征向量直接作为神经网络的输入。这个思路适合作为毕设的对比实验——传统模型和神经网络各代表一个方向但输入是同一套特征。# DNN.py 核心逻辑示例 from tensorflow import keras from tensorflow.keras import layers model keras.Sequential([ layers.Dense(32, activationrelu, input_shape(X_train.shape[1],)), layers.Dropout(0.3), layers.Dense(16, activationrelu), layers.Dense(1, activationsigmoid) ]) model.compile( optimizeradam, lossbinary_crossentropy, metrics[accuracy] ) model.fit(X_train, y_train, epochs30, batch_size32, validation_split0.2)这个结构是典型的小型MLP。第一层32个神经元加ReLU激活中间插一个Dropout防止过拟合第二层16个神经元继续压缩特征维度输出层用sigmoid做二分类。epochs设30batch_size设32validation_split从训练集里抽出20%做验证。DNN在这里的优势是能学到特征之间的复杂非线性关系比SVM和随机森林更灵活。但代价是需要调超参数训练时间也长。epochs设太大容易过拟合太小欠拟合常见做法是观察验证集loss的变化如果验证loss先降后升就是过拟合的信号需要加大Dropout比例或者减少epochs。如果跑的机器是CPU建议把epochs降到15左右训练速度和效果能平衡。5. 避坑与常见问题跑这个项目最容易翻车的五个点5.1 特征不标准化导致SVM性能崩塌现象SVM跑出来的准确率很低且分类报告里召回率异常某些类别的预测结果完全偏向一侧。原因特征量纲差异太大。URL长度动辄几百而敏感词计数只有个位数RBF核计算距离时距离被较大数值的特征主导小数值特征的作用被稀释掉。解决在训练前用StandardScaler对特征做标准化。注意是先fit训练集再用同一组参数transform测试集不能把测试集单独fit否则会引入信息泄露。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)5.2 sklearn版本不一致引发的API报错现象import sklearn或者实例化模型时报错提示某个参数名称不认识。原因sklearn版本差异不同版本之间部分参数名字有变动或者老版本不支持某些新参数。解决建议锁定版本我复现时用的是sklearn 1.2.2和Python 3.9整套流程跑下来没有接口问题。如果不想换版本就把代码里的参数名和当前版本的官网文档核对一下gammascale是0.22版本以后才加入的。5.3 DNN训练不收敛或验证loss是nan现象loss显示为nan或者准确率一直停在某个值附近不动。原因一是特征数值过大输入网络之前没有做归一化激活函数输出饱和导致梯度消失二是学习率设置过大adam优化器默认0.001在多数情况下没问题但如果特征分布特别宽仍可能震荡。解决先把特征做标准化和SVM的步骤一致再把epochs降到20同时观察训练集loss是否持续下降。如果还是nan加上一层BatchNormalization或者在Dense层前用MaxAbsScaler缩放到-1到1区间。5.4 样本量太小导致训练集和测试集结果差距过大现象训练集准确率接近100%测试集准确率只有六七十典型的过拟合特征。原因恶意网站数据集通常不大特征空间如果维数偏高模型很容易记住训练集里的噪声。解决换用随机森林并调大min_samples_leaf或者用K折交叉验证代替单次切分。交叉验证能让你看到模型在多个数据子集上的表现分布比单次切分更能反映真实泛化能力。5.5 可视化中文标签乱码现象matplotlib画出的图里中文标签全部变成方块。原因matplotlib默认字体不支持中文。解决显式指定字体或者改用英文标签。我一般图省事直接用英文标签毕设文档里再对应解释这样代码在任何机器上都能跑。plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False6. 把三个模型放到一起对比判断哪个结果真正可用跑完三个模型后不要只看准确率一个指标。恶意网站检测场景下漏报的代价远高于误报——一个恶意URL漏过去用户可能直接中招而误报最多是让用户访问不了正常站点。所以评价模型时要重点看召回率也就是恶意样本里有多少被正确识别出来。我习惯用classification_report输出每一项指标再把三个模型的结果拼成一个对比表不仅方便自己做决策放进毕业论文里也比较直观。对比维度包括准确率、精确率、召回率、F1值和训练时间。按这个项目的特征规模和样本量通常随机森林的F1分数最稳SVM次之DNN反而容易因为样本量不足而表现不稳定。进阶一点的思路是把三个模型做成投票集成让输出结果取多数表决。多数情况下三个模型投票一致时的预测置信度明显更高可以再加一层阈值控制——比如三个模型都判定为恶意才拦截这样能极大降低误报率适合部署在用户量大的生产环境。相反如果安全优先则只要有一个模型判定恶意就告警牺牲少量可用性换取安全性。最后我想说的一个习惯是每改一次特征或者调一次参都把结果记录成一个带时间戳的CSV内容包括特征版本、模型参数、准确率、召回率和训练时长。这个习惯救过我很多次因为调参过程中很容易出现上一版效果好、下一版改了特征反而变差的情况没有记录就得全部重跑一遍浪费时间不说还容易怀疑自己改错了地方。从那以后我每次做完一轮实验都会强制自己花两分钟写一行记录方便回看。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。