从Scrapy爬虫到情感分析模型:豆瓣电影评论数据全流程实战
简介:在数据科学和自然语言处理领域,文本情感分析是一项基础且应用广泛的技术,它旨在通过算法自动识别和提取文本中的主观情感倾向。其核心原理通常涉及文本预处理、特征工程和机器学习模型构建。这项技术的价值在于能够将海量非结构化文本转化为可量化的情感洞察,广泛应用于产品口碑监控、社交媒体舆情分析和内容推荐等场景。本文以豆瓣电影短评这一经典数据集为例,详细演示了如何通过Scrapy框架进行稳健的数据爬取,并利用TF-IDF等特征工程方法,结合逻辑回归等模型,构建一个端到端的中文文本情感分析解决方案。项目涵盖了数据清洗、探索性分析和模型评估等关键环节,为从事数据采集、情感分析或Python爬虫开发的实践者提供了一个完整的工程化参考案例。
1. 项目缘起:从数据获取到情感洞察的完整链路
最近在复盘一些经典项目时,我重新审视了“豆瓣电影TOP250”这个数据金矿。很多朋友入门爬虫、数据分析甚至NLP,都拿它练过手。但大多数教程止步于“把数据爬下来”,或者简单做个词云、画个评分分布图就结束了。这就像只挖到了矿石,却没有进行精炼和深加工,实在可惜。
这个项目的完整标题——“TOP250豆瓣电影短评:Scrapy 爬虫+数据清理/分析+构建中文文本情感分析模型”——清晰地勾勒出了一条从数据采集、预处理、探索分析,到最终构建一个可用的预测模型的完整数据科学流水线。它不是一个孤立的爬虫脚本,也不是一个单纯的分析报告,而是一个端到端的解决方案。其核心价值在于,它模拟了一个真实的数据产品开发流程:我们如何从互联网上获取非结构化的文本数据,经过一系列“脏活累活”的清洗和整理,将其转化为结构化的、可供分析的信息,并最终利用这些信息训练一个能够理解中文文本情感的模型。
对于数据从业者、产品经理,或者对影评分析感兴趣的朋友来说,这个项目极具参考意义。它不仅能让你掌握Scrapy框架的实战技巧,理解数据清洗中的各种“坑”,更能带你走完一个完整的数据分析闭环,并亲手搭建一个虽然基础但功能完整的NLP模型。接下来,我将结合我多次实施类似项目的经验,拆解其中的每一个环节,补充那些教程里通常不会写的细节和避坑指南。
2. 环境构筑与Scrapy爬虫实战:稳健获取数据源
数据是这一切的基石。豆瓣TOP250的短评数据量适中、质量相对较高,是绝佳的练手对象。但直接上手写爬虫很容易踩坑,我们需要先搭建一个稳健的工程环境。
2.1 工程化环境搭建与依赖管理
很多新手喜欢在全局环境里pip install,项目一多就容易版本冲突。我的习惯是为每个数据项目创建独立的虚拟环境。使用conda或venv都可以,这里以venv为例:
# 创建项目目录并进入 mkdir douban_sentiment_analysis && cd douban_sentiment_analysis # 创建虚拟环境 python -m venv venv # 激活虚拟环境 (Linux/macOS) source venv/bin/activate # 激活虚拟环境 (Windows) venv\Scripts\activate接下来是依赖管理。创建一个requirements.txt文件,提前规划好所需库及其版本,这能极大避免后续因版本不兼容导致的诡异错误。
# requirements.txt Scrapy==2.11.0 pandas==2.1.4 numpy==1.24.4 jieba==0.42.1 scikit-learn==1.3.2 matplotlib==3.8.2 seaborn==0.13.0 # 用于更复杂的文本处理或模型 # transformers==4.36.2 # torch==2.1.1然后一键安装:pip install -r requirements.txt。这一步看似简单,但确立好规范能让你在团队协作或项目复现时节省大量排查环境问题的时间。
2.2 Scrapy爬虫核心:策略、反爬与数据提取
使用Scrapy框架,而不仅仅是requests+BeautifulSoup,是为了利用其强大的异步处理、中间件管道和项目结构,这对于爬取数百页、数万条评论来说是更专业和高效的选择。
首先,初始化Scrapy项目:scrapy startproject douban_top250。项目结构会自动生成。核心在于编写爬虫文件(spider)。我们的目标是:遍历TOP250电影列表页,对每一部电影,进入其短评页,翻页爬取短评内容、评分、用户名、时间和有用数。
1. 爬虫策略设计:这里涉及“垂直型爬虫”和“增量型爬虫”的混合思路。对于TOP250列表,我们一次性爬取(批量型),但对于每部电影的短评,我们需要模拟翻页(垂直深入)。必须严格遵守网站的robots.txt(虽然豆瓣对部分API接口有限制),并为每个请求添加合理的延迟(如DOWNLOAD_DELAY = 2),这是基本的网络礼仪,也能有效降低被封IP的风险。
2. 应对反爬机制:豆瓣的反爬不算最严,但仍有基础防护。
- User-Agent:必须在
settings.py中设置一个合理的USER_AGENT列表,并在中间件中随机切换。 - Cookies:对于需要登录才能查看更多评论的场景(如查看更多短评),需要处理Cookie。但TOP250电影的短评前几页通常无需登录。重要提示:绝对不要尝试分享或使用他人的登录Cookie进行爬取,这涉及用户隐私和安全问题,也违反网站规则。我们的练习应仅限于公开可访问的数据。
- IP代理:对于大规模爬取,这是必须的。但在本练习中,通过控制请求频率(如每秒1次)和并发数,通常可以安全完成。可以在
settings.py中设置:CONCURRENT_REQUESTS = 1 DOWNLOAD_DELAY = 3 - 动态内容:豆瓣短评页是静态HTML,无需处理JavaScript渲染。但如果遇到动态加载,可以考虑使用
scrapy-splash或scrapy-playwright。不过根据最新实践,豆瓣主站目前用Scrapy直接解析HTML足矣。
3. 数据提取与Item定义:在items.py中定义清晰的数据结构,这有利于后续的数据处理。
import scrapy class DoubanCommentItem(scrapy.Item): movie_id = scrapy.Field() # 电影ID movie_title = scrapy.Field() # 电影名 comment_user = scrapy.Field() # 评论用户 comment_time = scrapy.Field() # 评论时间 comment_text = scrapy.Field() # 评论文本 comment_vote = scrapy.Field() # “有用”数 comment_star = scrapy.Field() # 评分(1-5星)在爬虫解析函数中,使用XPath或CSS选择器精准提取数据。例如,提取短评文本可能需要处理换行符和多余空格:comment = response.xpath('//span[@class="short"]/text()').get().strip()。
4. 数据存储:在pipelines.py中,我们可以将数据实时写入文件,比如JSON Lines格式(.jl),这种格式每行一个完整的JSON对象,非常适合后续用pandas读取。
import json class JsonWriterPipeline: def open_spider(self, spider): self.file = open('douban_comments.jl', 'w', encoding='utf-8') def close_spider(self, spider): self.file.close() def process_item(self, item, spider): line = json.dumps(dict(item), ensure_ascii=False) + "\n" self.file.write(line) return item别忘了在settings.py中启用这个Pipeline:ITEM_PIPELINES = { 'douban_top250.pipelines.JsonWriterPipeline': 300, }。
实操心得:爬虫运行时,务必在终端仔细观察日志。如果频繁出现403或302状态码,说明触发了反爬,需要立即停止,检查请求头、延迟设置,并考虑添加IP代理池。一个稳健的爬虫,其代码量可能只占30%,另外70%的精力都花在了应对各种反爬策略和异常处理上。
3. 数据清洗与预处理:从原始文本到规整数据
爬取到的原始数据就像刚从地里挖出来的土豆,沾满了泥土(HTML标签、特殊字符、不一致的格式)。数据清洗的目的就是把这些“土豆”洗干净、去皮、切成规整的块,方便后续“烹饪”(分析建模)。这一步往往比想象中更耗时,也更能体现数据工程师的功底。
3.1 脏数据识别与清洗策略
用pandas读取我们爬取的.jl文件:df = pd.read_json('douban_comments.jl', lines=True)。首先查看数据概览:df.info(),df.head(),df.isnull().sum()。
常见的“脏数据”问题及处理方案:
缺失值处理:
comment_star(评分)缺失:有些用户只评论不打分。我们可以将其填充为0(代表未评分),并在后续分析中区分对待。comment_text(评论)缺失:这是我们的核心特征,如果缺失,这条数据基本无用,直接删除:df = df.dropna(subset=['comment_text'])。comment_time格式混乱:可能是“2023-10-01”、“2023年10月1日”或时间戳。需要统一解析为datetime类型:df['comment_time'] = pd.to_datetime(df['comment_time'], errors='coerce')。errors='coerce'会将解析失败的变成NaT(Not a Time),方便后续查看。
异常值处理:
comment_star:理论上应为1-5的整数。检查是否有0或大于5的值,这些可能是爬虫解析错误,需要根据情况修正或删除。comment_vote(有用数):应为非负整数。检查是否有负数或异常大的数(可能是爬虫错误或数据异常)。
文本数据清洗: 这是中文文本处理的重头戏。我们需要创建一个文本清洗函数,对
comment_text字段进行流水线处理。import re import jieba def clean_text(text): if not isinstance(text, str): return '' # 1. 去除HTML标签和URL text = re.sub(r'<[^>]+>', '', text) text = re.sub(r'http[s]?://\S+', '', text) # 2. 去除特殊字符、数字、英文(根据情感分析目标决定) # 如果认为数字和英文对情感无影响,可以去掉 text = re.sub(r'[a-zA-Z0-9]', '', text) # 保留中文、中文标点 text = re.sub(r'[^\u4e00-\u9fa5,。!?;:“”‘’\-\s]', '', text) # 3. 合并多余空白字符 text = re.sub(r'\s+', ' ', text).strip() return text df['comment_cleaned'] = df['comment_text'].apply(clean_text)重复数据去重: 同一用户对同一电影可能多次评论(罕见),或爬虫因翻页逻辑问题导致重复抓取。我们需要基于关键字段去重:
df = df.drop_duplicates(subset=['movie_id', 'comment_user', 'comment_text', 'comment_time'])
3.2 中文分词与停用词过滤
清洗后的文本需要分词,才能进行后续的向量化。我们使用jieba库。
def segment_text(text): # 使用精确模式分词 words = jieba.lcut(text) # 加载停用词表 # 可以从 https://github.com/goto456/stopwords 获取中文停用词表 with open('stopwords.txt', 'r', encoding='utf-8') as f: stopwords = set([line.strip() for line in f]) # 去除停用词和单字词(通常信息量低) words = [w for w in words if w not in stopwords and len(w) > 1] return ' '.join(words) # 用空格连接,方便后续CountVectorizer使用 df['comment_segmented'] = df['comment_cleaned'].apply(segment_text)这里有个坑:停用词表的选择很重要。通用的停用词表会去掉“不”、“没有”等否定词,但这在情感分析中是极其重要的信号!因此,你需要根据情感分析的任务,自定义或谨慎选择停用词表,或者干脆在后续的n-gram特征中捕捉这些否定关系。
3.3 构建情感分析标签
我们的目标是监督学习,所以需要标签。豆瓣短评自带1-5星的评分(comment_star),这是一个完美的情感标签来源。我们可以将其转换为二分类或三分类问题:
- 二分类(积极/消极):通常将4-5星视为积极(1),1-2星视为消极(0),3星作为中性可以剔除或单独处理。
df = df[df['comment_star'].isin([1,2,4,5])] # 剔除3星 df['sentiment'] = df['comment_star'].apply(lambda x: 1 if x >= 4 else 0) - 三分类(积极/中性/消极):1-2星为消极(0),3星为中性(1),4-5星为积极(2)。
经验之谈:数据清洗没有“标准答案”。你需要根据数据的具体情况和分析目标反复迭代。清洗完后,务必保存一份干净的中间数据(如df.to_csv(‘cleaned_comments.csv’, index=False, encoding=‘utf-8-sig’)),避免每次从头开始清洗。
4. 探索性数据分析:在建模前读懂你的数据
在急于把数据塞进模型之前,花时间进行探索性数据分析是至关重要的。这能帮你理解数据分布、发现潜在问题、获得特征工程的灵感,甚至可能推翻你最初的假设。
4.1 数据分布与基本统计
首先看下整体数据规模、情感标签的分布是否均衡。
import matplotlib.pyplot as plt import seaborn as sns print(f"总评论数: {len(df)}") print(f"情感分布:\n{df['sentiment'].value_counts()}") # 可视化 plt.figure(figsize=(8,5)) sns.countplot(x='sentiment', data=df) plt.title('Sentiment Distribution') plt.show()如果正负样本极度不均衡(比如9:1),后续就需要考虑过采样(如SMOTE)、欠采样或在模型评估时使用F1-score、AUC-ROC等指标,而不是简单的准确率。
查看评分(comment_star)的分布,可以验证我们将其转换为情感标签的合理性。通常,豆瓣评分呈现“J”型或“L”型分布,即高分居多,低分其次,中间分较少。
4.2 文本长度分析
评论文本的长度是一个重要特征。过短的评论(如“好”、“烂”)可能信息量不足,过长的可能是复制粘贴的影评。
df['text_length'] = df['comment_cleaned'].apply(len) print(df['text_length'].describe()) plt.figure(figsize=(10,5)) plt.hist(df['text_length'], bins=50, edgecolor='black') plt.xlabel('Text Length (characters)') plt.ylabel('Frequency') plt.title('Distribution of Comment Length') plt.show()这个分析可以帮助你决定在后续向量化时是否要设置max_features(最大特征数)或截断/填充文本长度。
4.3 关键词与词云可视化
通过词频统计和词云,可以直观感受积极和消极评论分别关注什么。
from wordcloud import WordCloud # 分离积极和消极评论 positive_text = ' '.join(df[df['sentiment']==1]['comment_segmented']) negative_text = ' '.join(df[df['sentiment']==0]['comment_segmented']) # 生成词云 wordcloud_pos = WordCloud(font_path='SimHei.ttf', width=800, height=400, background_color='white').generate(positive_text) wordcloud_neg = WordCloud(font_path='SimHei.ttf', width=800, height=400, background_color='black').generate(negative_text) # 并排显示 fig, axes = plt.subplots(1, 2, figsize=(16, 8)) axes[0].imshow(wordcloud_pos, interpolation='bilinear') axes[0].set_title('Positive Reviews Word Cloud') axes[0].axis('off') axes[1].imshow(wordcloud_neg, interpolation='bilinear') axes[1].set_title('Negative Reviews Word Cloud') axes[1].axis('off') plt.show()你可能会发现,积极评论中高频出现“经典”、“演技”、“故事”、“感动”,而消极评论中则可能出现“无聊”、“尴尬”、“失望”、“剧情”。这验证了我们的任务可行性,也为特征选择提供了方向。
4.4 评论时间与投票数分析
分析评论时间(comment_time)的分布,可以看到哪些年份或月份评论活跃,是否与电影上映时间有关。分析“有用”数(comment_vote)的分布,可以观察高质量评论(高投票)的情感倾向是否有别于普通评论。这些都可以作为后续建模的辅助特征。
EDA的核心思想:不是画完图就结束,而是要对每一个发现提出问题。例如,“为什么文本长度分布有双峰?”,“这两个情感类别的词云差异是否足够明显?”,“样本不均衡会对我的模型造成多大影响?”。这些问题将直接指导你进入下一个阶段——特征工程与模型构建。
5. 特征工程与文本向量化:将文字转化为数字
计算机无法直接理解“这部电影真是绝了”这句话,我们必须将文本转换成它擅长的数字形式。这个过程就是特征工程,对于文本模型来说,其质量直接决定了模型性能的天花板。
5.1 文本向量化方法选择
我们有多种工具可以将分词后的文本(comment_segmented)转化为特征向量。
词袋模型与TF-IDF:
- CountVectorizer:最简单的词袋模型,统计每个词在文档中出现的次数。它的问题是会给高频但无意义的词(如“电影”、“的”)过高权重。
- TfidfVectorizer:在词袋基础上,加入了“逆文档频率”(IDF)惩罚。一个词如果在所有文档中都常见(如“电影”),其权重会被降低;如果只在某些文档中出现(如“烧脑”),其权重会提高。这通常比纯词袋模型效果更好。
from sklearn.feature_extraction.text import TfidfVectorizer # 初始化TF-IDF向量化器 # max_features可以限制特征维度,避免维度过高 tfidf = TfidfVectorizer(max_features=5000, ngram_range=(1, 2)) # ngram_range=(1,2) 表示同时考虑单个词和两个词的组合(二元语法) # 例如,“不错”和“非常不错”会被视为不同的特征 X_tfidf = tfidf.fit_transform(df['comment_segmented']) y = df['sentiment'].values使用
ngram_range=(1,2)是个好习惯,它能捕捉像“不 好”这样的否定短语,这对情感分析至关重要。词向量均值: 使用预训练的词向量模型(如腾讯AI Lab的
Tencent_AILab_ChineseEmbedding或word2vec/fastText训练好的模型),将句子中每个词的向量取平均值,作为句子的表示。这种方法能捕捉一定的语义信息,但丢失了词序。# 假设已加载预训练词向量模型 `model` def get_sentence_vector(sentence, model): words = sentence.split() vectors = [model[w] for w in words if w in model] if len(vectors) > 0: return np.mean(vectors, axis=0) else: return np.zeros(model.vector_size) X_emb = np.array([get_sentence_vector(s, model) for s in df['comment_segmented']])深度学习模型的特征: 如果打算用深度学习模型(如LSTM, Transformer),通常不需要手动做复杂的向量化,只需要构建词索引,然后使用嵌入层(Embedding Layer)让模型在训练中学习词向量。
对于本项目,从简单有效出发,我推荐使用TF-IDF + 二元语法作为起点。它实现简单,效果稳定,且能很好地捕捉情感关键词和短语。
5.2 特征选择与降维
经过TF-IDF向量化后,我们可能得到数千甚至上万个特征(词或短语)。其中很多是噪音。我们可以进行特征选择来提升模型效率和效果。
- 基于统计量的选择:使用
chi2(卡方检验)或mutual_info_classif(互信息)来选择与情感标签最相关的K个特征。from sklearn.feature_selection import SelectKBest, chi2 # 假设 X_tfidf 是之前的TF-IDF矩阵 selector = SelectKBest(chi2, k=3000) # 选择3000个最佳特征 X_selected = selector.fit_transform(X_tfidf, y) - 基于模型的选择:使用带L1正则化的线性模型(如
LogisticRegression(penalty=‘l1’)),训练后,权重非零的特征即为被选中的特征。
对于维度特别高的情况,也可以使用主成分分析(PCA)或截断奇异值分解(TruncatedSVD)进行降维,但要注意这些线性降维方法可能会损失非线性信息。
实操建议:在项目初期,可以先用全部特征或选择前5000-10000个特征训练一个基线模型。如果训练速度过慢或出现过拟合,再考虑进行更激进的特征选择或降维。
6. 构建与评估情感分析模型
特征准备好后,我们就可以开始训练模型了。我们从简单的经典模型开始,逐步尝试更复杂的模型,并建立一个可靠的评估流程。
6.1 数据集划分
首先,必须将数据划分为训练集、验证集和测试集。千万不能用全部数据训练后又用同样的数据测试,那会得到完全不可信的、虚高的准确率。
from sklearn.model_selection import train_test_split # X_selected 是经过特征选择后的特征矩阵,y是标签 X_train, X_temp, y_train, y_temp = train_test_split(X_selected, y, test_size=0.3, random_state=42, stratify=y) X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp) # 现在我们有:60%训练,20%验证,20%测试 # stratify=y 确保划分后各类别比例与原数据集一致,对于不均衡数据很重要6.2 模型选择与训练
逻辑回归:线性模型的标杆,解释性强,训练快,是很好的基线模型。
from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report, accuracy_score, confusion_matrix lr_model = LogisticRegression(max_iter=1000, random_state=42, class_weight='balanced') # class_weight处理不均衡 lr_model.fit(X_train, y_train) y_val_pred = lr_model.predict(X_val) print(classification_report(y_val, y_val_pred))朴素贝叶斯:特别适合文本分类,计算效率高。
MultinomialNB适用于离散特征(如词频)。from sklearn.naive_bayes import MultinomialNB nb_model = MultinomialNB() nb_model.fit(X_train, y_train)支持向量机:在高维空间(如TF-IDF特征)中往往表现优异,但训练速度相对较慢。
from sklearn.svm import LinearSVC svm_model = LinearSVC(random_state=42, class_weight='balanced') svm_model.fit(X_train, y_train)随机森林/XGBoost:基于树的集成模型,能捕捉非线性关系,且对特征缩放不敏感。
from sklearn.ensemble import RandomForestClassifier rf_model = RandomForestClassifier(n_estimators=100, random_state=42, class_weight='balanced') rf_model.fit(X_train, y_train)
6.3 模型评估与调优
不要只看准确率!尤其在我们的数据可能不均衡的情况下。
核心评估指标:
- 精确率:预测为积极的评论中,真正积极的比例。(“查得准不准”)
- 召回率:所有真实的积极评论中,被模型找出来的比例。(“查得全不全”)
- F1-score:精确率和召回率的调和平均数,是综合衡量指标。
- AUC-ROC:衡量模型将正例排在前面的能力,对类别不平衡不敏感。
from sklearn.metrics import precision_recall_fscore_support, roc_auc_score precision, recall, f1, _ = precision_recall_fscore_support(y_val, y_val_pred, average='binary') auc = roc_auc_score(y_val, lr_model.predict_proba(X_val)[:, 1]) print(f"Precision: {precision:.3f}, Recall: {recall:.3f}, F1: {f1:.3f}, AUC: {auc:.3f}")交叉验证与网格搜索: 使用验证集初步评估后,应用交叉验证来更稳健地评估模型,并用网格搜索寻找最优超参数。
from sklearn.model_selection import GridSearchCV param_grid = {'C': [0.01, 0.1, 1, 10, 100]} grid_search = GridSearchCV(LogisticRegression(max_iter=1000, class_weight='balanced', random_state=42), param_grid, cv=5, scoring='f1') grid_search.fit(X_train, y_train) print(f"Best parameters: {grid_search.best_params_}") print(f"Best CV score: {grid_search.best_score_:.3f}")混淆矩阵可视化: 直观查看模型在哪个类别上犯错最多。
from sklearn.metrics import ConfusionMatrixDisplay disp = ConfusionMatrixDisplay.from_estimator(lr_model, X_val, y_val, display_labels=['Negative', 'Positive']) disp.plot() plt.show()
经验分享:在文本分类任务中,线性模型(逻辑回归、线性SVM)配合TF-IDF特征,常常能取得与更复杂模型媲美甚至更好的效果,而且训练和预测速度极快。我的建议是,先用逻辑回归或线性SVM建立一个强基线,如果效果不满足要求,再考虑更复杂的模型或特征。深度学习模型(如LSTM, BERT)虽然强大,但需要更多的数据、更长的训练时间和计算资源,对于豆瓣TOP250短评这个量级的数据(约数万条),经典机器学习方法往往是性价比最高的选择。
7. 模型部署与简易应用示例
训练出一个满意的模型后,我们可以将其保存下来,并构建一个简单的流水线,用于预测新的电影短评。
7.1 模型与向量化器的持久化
我们需要保存两个东西:训练好的模型(model)和拟合数据后的TF-IDF向量化器(tfidf)。这样在预测新评论时,才能用同样的词典进行转换。
import joblib # 或 pickle # 假设 best_model 是网格搜索得到的最佳模型 best_model = grid_search.best_estimator_ # 保存模型和向量化器 joblib.dump(best_model, 'sentiment_model.pkl') joblib.dump(tfidf, 'tfidf_vectorizer.pkl') # 如果做了特征选择,也需要保存 selector joblib.dump(selector, 'feature_selector.pkl')7.2 构建预测流水线
创建一个预测函数,它封装了从原始文本到情感预测的完整步骤。
def predict_sentiment(raw_comment): """ 预测单条电影短评的情感倾向。 参数: raw_comment (str): 原始评论文本。 返回: tuple: (预测情感, 积极概率)。情感: 0-消极,1-积极。 """ # 1. 加载保存的组件 tfidf_loaded = joblib.load('tfidf_vectorizer.pkl') selector_loaded = joblib.load('feature_selector.pkl') model_loaded = joblib.load('sentiment_model.pkl') # 2. 文本清洗与分词 (复用之前的函数) cleaned_text = clean_text(raw_comment) segmented_text = segment_text(cleaned_text) # 3. TF-IDF 转换 tfidf_vector = tfidf_loaded.transform([segmented_text]) # 注意是列表形式 # 4. 特征选择 (如果之前做了) selected_vector = selector_loaded.transform(tfidf_vector) # 5. 预测 prediction = model_loaded.predict(selected_vector)[0] # 获取预测为积极的概率(如果模型支持) if hasattr(model_loaded, 'predict_proba'): proba = model_loaded.predict_proba(selected_vector)[0][1] else: proba = None sentiment_label = '积极' if prediction == 1 else '消极' return sentiment_label, proba # 测试 test_comments = ["这部电影真的太棒了,演员演技在线,剧情扣人心弦!", "无聊透顶,浪费了我两个小时的生命。"] for comment in test_comments: label, proba = predict_sentiment(comment) print(f"评论: {comment[:30]}... -> 预测情感: {label}, 积极概率: {proba:.3f}")7.3 可能的改进方向与局限
这个项目构建的模型是一个入门级的演示。在实际应用中,需要考虑以下方面进行改进:
- 领域适应性:这个模型是在“电影短评”语料上训练的,如果拿去分析产品评论或社交媒体情绪,效果可能会下降。需要考虑迁移学习或重新训练。
- 上下文与讽刺识别:模型很难理解“这部电影好到让我睡着了”这种反讽。更先进的模型(如基于Transformer的BERT)在这方面有更强能力,但需要更多数据和算力。
- 细粒度情感:目前是二分类(积极/消极)。可以尝试五分类(对应1-5星),或者识别更具体的情感(如“感动”、“愤怒”、“失望”)。
- 实时性与扩展性:如果要做成服务,需要考虑使用
Flask/FastAPI搭建API,用Redis做缓存,并部署在云服务器上。
这个从爬虫到建模的完整项目,其价值不仅在于最终的模型准确率,更在于你走通了数据科学的全流程,理解了每个环节的挑战和解决方案。下次当你面对一个新的数据问题时,这套方法论将成为你最有力的工具。
本文还有配套的精品资源,点击获取
