
简介这是一份面向计算机专业学生与毕业设计者的Python机器学习实战项目整个项目以97分通过毕业设计评审聚焦电商淘宝商品评论情感分析从Selenium模拟登录爬取评论到文本去噪、jieba分词、词向量化再到SVM与LSTM模型对比覆盖了情感分析项目的全链路流程。资源压缩包共43个文件以14个Python脚本为核心配有7个CSV数据表、2个Excel语料、训练好的h5模型与pkl词向量以及GUI界面、chromedriver和配置说明约66.68MB目录按爬虫、预处理、建模、测试等模块划分便于系统复现。目前已有382人学习下载。此外还内置LSTM/SVM两套模型文件、可视化绘图脚本和预处理工具能够帮助读者理解电商评论情感分析的建模思路与调参细节。整个资源适合作为课程设计、大作业或毕业设计的参考脚手架尤其适合有一定Python基础、希望完整走通NLP流程的学习者。1. 从淘宝评论到情感标签拆解这个97分的Python机器学习毕设几万条淘宝评论堆在数据库里光看“好评率”不够运营要的是“为什么差评”。我见过太多人一上来就调模型结果在Selenium登录和jieba词典上先卡一整天。这个 97 分的毕业设计项目把整条链路跑通了Selenium模拟登录抓淘宝评论jieba精确模式分词Word2vec把词语转成向量SVM与LSTM各出一版分类器最后用GUI页面点一下就能出结果。适合正在做客服评价分析、课程设计和毕业设计的人——他们缺的往往不是模型公式而是一段从爬虫到可视化都可直接复用、能改能跑的代码。下面按数据流顺序拆开讲附带我在复现时的参数权衡。2. 数据链路搭建Selenium登录爬虫与jieba分词清洗的工程细节2.1 用Selenium模拟真实登录的爬虫方案爬淘宝评论最大的门槛不是反爬策略而是登录态。常见做法是用Selenium加载登录页、人工扫码把登录Cookie留在浏览器上下文里后续请求就无需再走验证码。项目里restaurant_crawler.py和crawler.py基本是同一套思路核心代码如下from selenium import webdriver from selenium.webdriver.chrome.options import Options import time options Options() options.add_argument(--disable-blink-featuresAutomationControlled) # 去掉 webdriver 特征避免被淘宝识别成自动化脚本 options.add_experimental_option(excludeSwitches, [enable-automation]) driver webdriver.Chrome(optionsoptions) driver.get(https://login.taobao.com) input(扫码登录完成后按回车继续...) # 这一步模拟真实登录行为 for page in range(5): driver.get(f商品详情页URL?page{page}) time.sleep(2) # 等待动态加载 driver.execute_script(window.scrollTo(0, document.body.scrollHeight)) time.sleep(1) items driver.find_elements_by_css_selector(.tmpl-rev-item) for it in items: txt it.find_element_by_css_selector(.tmpl-rev-content).text print(txt)--disable-blink-featuresAutomationControlled的作用是关闭Chromium的自动化控制标记能减少JS检测拦截。time.sleep(2)不是摆设淘宝评论模块是异步渲染的睡眠不足会抓到空列表。翻页建议直接构造URL参数比模拟点击“下一页”稳定得多如果评论区走Ajax也可以直接用requests带上Cookie拿JSON接口但毕设项目用Selenium展示完整登录态更直观。2.2 评论文本清洗去标点、去“666”这类无效短语爬下来的评论噪音很大特别是“666”“好好好”“。。。”这类无意义内容会直接污染词向量。清洗步骤写在review_treatment/review_pretreatment.py里做法是先去空白再过滤黑名单词最后统一去掉非中文、非数字、非字母的符号import re drop_words {666, 好好好, 哈哈, 。。, 。。。, 不错不错} def clean_comment(text: str) - str: text text.strip() if text in drop_words: return # 只保留中文、英文、数字去掉标点 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9], , text) return text注意正则里的\u4e00-\u9fa5只匹配简体中文字符范围繁体字会被误删。如果语料里混有港澳台评论建议扩展为\u4e00-\u9fff。drop_words用集合而不是列表in判断是O(1)几万条评论时差异很明显。清洗后的结果分别落进pos.csv、neg.csv、neutral.csv这三个文件是后续训练集的来源。2.3 jieba精确模式分词与词典构造jieba默认是精确模式等价于cut_allFalse它会把“这个商品质量很好”切成“这个/商品/质量/很好”对情感分析最合适。全模式会把词切碎成“商品/品质”这种带重复的片段搜索引擎模式更不适合直接训练。下图是三种模式的差异分词模式jieba参数对情感分析的影响精确模式jieba.lcut(text)词边界干净适合词向量训练全模式jieba.lcut(text, cut_allTrue)产生冗余词扩大词表但引入噪音搜索引擎模式jieba.lcut_for_search(text)细粒度切分通常用于检索引擎项目里加载词典用comment_text.model这是jieba训练出来的自定义词典模型里面包含电商领域词例如“性价比”“发货速度”“客服态度”等。没有这些词jieba就会把“性价比”切成“性价/比”词向量语义直接偏掉。import jieba jieba.load_userdict(data/comment_text.model) text 性价比很高但是发货速度太慢了 words jieba.lcut(text, cut_allFalse) # 输出: [性价比, 很, 高, 但是, 发货速度, 太慢, 了]load_userdict加载的行格式是词语 词频 词性词频写0会让jieba自动推断。自定义词典必须放在分词之前初始化否则已经切好的词不会重新合并。清洗和分词完成后结果写入train_cut.csv这就是后续词向量和分类器的输入。3. 词向量化从Word2vec训练到comment_text.vector的生成3.1 为什么这里选Word2vec而不是TF-IDF统计派做法是TF-IDF加权词频矩阵能表达“哪些词重要”但表达不了“哪些词语义接近”。在电商评论场景里“贵”和“价格高”是不同字面、相同语义TF-IDF会把它俩拆成两个毫无关系的特征维度。Word2vec训练出的词向量让“贵”“便宜”“划算”在向量空间里落在相近区域SVM和LSTM拿到的输入也就更接近人的语感。项目里已生成Word2vec_model.pkl如果你要复现语料量少于1万条时建议直接用现成模型。3.2 训练参数维度128、窗口5、skip-gramfrom gensim.models import Word2Vec # sentences 是分词后的二维列表例如 [[性价比, 高], ...] sentences load_tokenized_lines(data/train_cut.csv) w2v_model Word2Vec( sentencessentences, vector_size128, # 词向量维度电商领域128够用 window5, # 前后5个词构成上下文 min_count2, # 出现次数少于2次的词丢弃 sg1, # 1skip-gram, 0CBOW epochs10, workers4 ) w2v_model.save(Word2vec_model.pkl)sg1选择skip-gram在小规模语料上对低频词更友好代价是训练时间略高。window5意味着“便宜”和距离5个词以内的“质量”会产生关联窗口太小无法捕捉较长否定结构“没有想象的那么差”窗口太大又会引入无关词。min_count2过滤掉只出现一次的噪点词词典规模会缩小20%-40%但模型稳定性明显上升。3.3 从词向量到句子索引comment_text.vector的生成训练好词向量后需要把每条评论变成一个定长序列给LSTM吃。项目里test/Word2vec_model.pkl和comment_text.vector配合使用前者的模型提供了wv.index_to_key后者保存了词语索引与句子索引的映射import numpy as np from tensorflow.keras.preprocessing.sequence import pad_sequences vocab list(w2v_model.wv.index_to_key) word2idx {w: i for i, w in enumerate(vocab)} # 每条评论分词后的词映射成序号 seq [word2idx[w] for w in words if w in word2idx] seq pad_sequences([seq], maxlen64, paddingpost, truncatingpost) # paddingpost 在句子末尾补0truncatingpost 截断句子末尾这里补0和截断都放在尾部是为了保住句首的情感焦点词。句首往往是“质量”“物流”“卖家”这类主题词句尾更多是“很好”“差劲”这类评价词如果你更重视评价词可以改成pre。word2idx编码后的序列、w2v_model.wv对应的向量矩阵共同构成Embedding层的初始化权重。项目里Word2vec_model.pkl和comment_text.vector是配套产物comment_text.vector存储的就是每条评论的长度和向量化结果本质是后面SVM训练矩阵的缓存。4. 分类模型对比SVM与LSTM的训练流程、调参与评估4.1 SVM基线把词向量均值化后喂给分类器SVM不能直接吃变长序列常规做法是把一条评论中所有词的向量取平均压成128维固定向量。损失了词序但换来了训练速度和稳定性。项目里svm/train_svm.py的核心逻辑如下import numpy as np from sklearn.svm import SVC from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, f1_score def sentence_embedding(words, w2v, word2idx): vectors [w2v.wv[w] for w in words if w in word2idx] if not vectors: return np.zeros(128) return np.mean(vectors, axis0) X np.array([sentence_embedding(s, w2v_model, word2idx) for s in train_cut]) y np.array(labels) # 从 pos.csv / neg.csv 读取的0/1标签 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy) svm SVC(kernelrbf, C1.0, gammascale, probabilityTrue) svm.fit(X_train, y_train) y_pred svm.predict(X_test) print(SVM acc:, accuracy_score(y_test, y_pred)) print(SVM f1:, f1_score(y_test, y_pred))kernelrbf配合gammascale是文本分类的稳妥起点C1.0控制误分类惩罚。评论数据如果包含三分类SVC默认是one-vs-one策略不会报错但f1建议用averagemacro看。probabilityTrue会触发Platt缩放训练时间变长如果不需要分类概率可以去掉。SVM跑在CPU上几千条样本通常几秒到几十秒出结果非常适合作为毕设里的基线模型。4.2 LSTM情感分类Embedding初始化、mask_zero与DropoutLSTM比SVM多一个层面的信息——词序它能捕捉“不怎么样”和“怎么样”这种否定词紧贴评价词的局部结构。项目里lstm/train_lstm.py定义了基础的Keras模型结构from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout from tensorflow.keras.preprocessing.sequence import pad_sequences max_len 64 X_seq pad_sequences(train_seqs, maxlenmax_len, paddingpost, truncatingpost) # 用Word2vec的向量矩阵初始化Embedding embedding_matrix np.array([w2v_model.wv[w] for w in vocab]) model Sequential() model.add(Embedding( input_dimlen(vocab), output_dim128, weights[embedding_matrix], trainableFalse, # 固定词向量防止小语料上过拟合 mask_zeroTrue )) model.add(LSTM(128, dropout0.2, recurrent_dropout0.2)) model.add(Dropout(0.2)) model.add(Dense(1, activationsigmoid)) model.compile(optimizeradam, lossbinary_crossentropy, metrics[accuracy]) model.fit(X_seq, y_train, batch_size64, epochs8, validation_split0.1)trainableFalse是关键毕设语料通常只有几千条微调整个Embedding矩阵极易过拟合固定词向量让LSTM只学句子结构。mask_zeroTrue配合paddingpost保证补零位置不参与计算。recurrent_dropout0.2会明显拖慢训练速度这是Native LSTM实现的固有限制若训练太慢可以去掉recurrent_dropout只留普通dropout。4.3 训练产物与测试入口项目里lstm_test.py是单条评论的推理入口from tensorflow.keras.models import load_model model load_model(lstm_three.h5) test_text 物流快包装也好下次还来 words jieba.lcut(test_text, cut_allFalse) seq pad_sequences([[word2idx[w] for w in words if w in word2idx]], maxlen64) score model.predict(seq)[0][0] print(正向概率: {:.2f}.format(score))lstm_three.h5和lstm_new.h5是两次不同结构的训练产物两者层数一样但超参数有差异。lstm.yml是Keras的网络结构定义文件配合h5可以了解模型输入输出的shape。如果你重装环境后模型加载报错常见是Keras 2.x和TensorFlow 2.x的版本兼容问题直接用load_model失败时改用model_from_yaml加载lstm.yml再load_weights权重。4.4 SVM与LSTM的取舍结论维度SVMLSTM输入特征128维均值向量最多64个词索引词序信息丢失保留训练速度秒级分钟级CPU/GPU依赖CPU即可建议GPU小样本表现更稳定容易过拟合最终模型文件svm_model/model.pkllstm_three.h5实际使用时我一般先用SVM跑通全流程确认数据没有标签错位再训练LSTM调精度。项目README里强调“两个模型可切换”原因是SVM在样本量3000以下时表现经常不输给LSTMLSTM则适合补充SVM没有捕捉到的否定结构。5. 复现排错chromedriver、模型路径与数据格式的坑5.1 chromedriver与浏览器版本错配Selenium最常见的启动失败是session not created: This version of ChromeDriver only supports Chrome version XX。项目根目录放了chromedriver但它对应当前系统里的Chrome主版本主版本不一致就得去下载对应驱动而不是改代码。检查命令# Linux/macOS chromedriver --version google-chrome --version # 或 chromium --version版本前两位必须一致比如Chrome 126就要配126.x的chromedriver。Windows下如果驱动不在当前目录需要显式指定路径driver webdriver.Chrome(executable_pathchromedriver.exe, optionsoptions)5.2 pkl模型加载的Python版本匹配svm_model/model.pkl、w2v_model.pkl、Word2vec_model.pkl都是pickle序列化Python 3.6以下和3.7以上的pickle协议有差异跨环境加载可能报UnpicklingError。项目用Python 3.7跑通我一般会在加载处加一层异常兜底import pickle def load_model_safe(path): with open(path, rb) as f: try: return pickle.load(f) except UnicodeDecodeError: # Python2 产生的 pickle 需要指定 encoding return pickle.load(f, encodinglatin1)5.3 CSV编码与标签错位data/pos.csv、data/neg.csv、test_set.csv大部分来自不同爬虫批次编码可能是utf-8也可能是gbk。用pandas读的时候建议显式指定引擎和编码import pandas as pd df pd.read_csv(data/train_cut.csv, encodingutf-8, enginepython) print(df[label].value_counts())标签错位是最隐蔽的坑pos.csv和neg.csv单独读取后如果拼接顺序不对训练集前一半全是1、后一半全是0模型看似acc很高实际只是记住了位置。我在每次train前都会用stratifyy切分再打印一遍value_counts确认分布。5.4 GUI加载模型时的相对路径问题GUI/main_page.py里有加载词向量和分类器的逻辑最容易错的是相对路径。直接双击执行时当前工作目录未必在项目根目录最好在代码开头切换到脚本所在目录import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) os.chdir(BASE_DIR)main_page.py调用的model_test.py里同时引用了Word2vec_model.pkl、lstm_three.h5、svm_model/model.pkl这些文件路径全是相对路径不切目录就会FileNotFoundError。GUI启动后先在控制台观察路径输出比打开页面后发现预测按钮报错要省时间。6. 改造技巧把情感分析复用到京东评论、三分类与FastText场景6.1 替换商城页面元素与选择器crawler.py里的CSS选择器写的是淘宝评论的tmpl-rev-item换到京东时评论条目变成.comment-item内容字段是.short-comment。改动集中在选择器部分items driver.find_elements_by_css_selector(.comment-item) for it in items: txt it.find_element_by_css_selector(.short-comment).text京东的评论区也是异步滚动加载滚动逻辑可以直接复用淘宝版的scrollTo脚本。拼多多则需注意.review-item结构且部分评论默认折叠需要先点“展开更多”。6.2 把二分类改成三分类原始数据里有neutral.csv但训练代码按二分类写。改成三分类时标签映射为pos0, neutral1, neg2LSTM输出层和损失函数都要换from tensorflow.keras.utils import to_categorical y_train_cat to_categorical(y_train, num_classes3) # 模型输出层 model.add(Dense(3, activationsoftmax)) model.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy])SVM侧把SVC()的decision_function_shape改为ovo预测结果直接是整数类别。测试时不要再用f1_score(averagebinary)改成averagemacro否则会报错。6.3 用FastText替代Word2vecSVM串联如果你不想维护Word2vec和分类器两个文件可以直接把训练语料转成FastText格式一条样本一行前缀__label__pos/__label__negecho __label__pos 性价比 很 高 train.txt python -m fasttext supervised -input train.txt -output model -epoch 20 -wordNgrams 2FastText训练完成后只需一个model.bin推理时model.predict(发货 速度 太慢)直接返回标签和置信度。它把词向量和线性分类器合在一起训练训练时间比LSTM少一个数量级在5000条评论规模的课程设计里wordNgrams2就能捕捉到“太慢”“很快”这类短语级特征。替换时注意FastText对空格分词敏感分词后要把句子用空格重新拼接否则中文会整句黏在一起。本文还有配套的精品资源点击获取