
最近在整理足球数据分析项目时常常需要从各类体育新闻、社交媒体和评论中提取情感倾向。这让我想起了一句充满画面感的诗“夜深忽梦少年事梦啼妆泪红阑干”它精准地描绘了那种复杂、深沉、交织着回忆与感伤的情绪。这种情绪分析不正是自然语言处理NLP中情感分析技术的用武之地吗而提到足球世界里的“少年事”与复杂情感克里斯蒂亚诺·罗纳尔多C罗与曼联的两次“牵手”与“离别”无疑是绝佳的分析案例。本文将以“曼联与C罗”这一充满话题性的主题为背景手把手带你构建一个完整的情感分析系统。我们将从网络爬虫抓取相关中文评论开始经过数据清洗、文本预处理再到使用预训练模型进行情感分类最后进行可视化分析。无论你是想学习NLP实战还是对足球数据分析感兴趣这篇文章都将提供一套可复现的代码和清晰的思路。1. 情感分析背景与项目目标情感分析又称观点挖掘是自然语言处理的一个经典分支。它的目标是自动识别和提取文本中的主观信息如态度、情绪、评价等。通常情感极性被分为三类正面、负面和中性。更细粒度的分析还可以识别具体的情绪如喜悦、愤怒、悲伤等。为什么选择“曼联C罗”作为分析对象话题性强C罗两度效力曼联从初出茅庐的天才少年到功成名就的游子归家再到不甚愉快的二次离别其过程充满了戏剧性和情感张力相关讨论文本的情感表达非常丰富和多元。数据易得在中文互联网上关于此话题的新闻、论坛帖子和评论数量庞大便于我们采集到足量的样本数据。实践意义通过这个具体案例我们可以完整走通一个NLP项目的全流程数据获取 - 预处理 - 模型应用 - 分析洞察。这比分析一个通用的商品评论数据集更有趣也更能体现技术的应用价值。本项目核心目标技术层面构建一个端到端的中文文本情感分析流水线。分析层面尝试量化分析中文社区在不同时期如C罗回归官宣时、离队新闻爆发时对“曼联C罗”话题的情感倾向变化。学习层面掌握使用requests、BeautifulSoup进行定向爬虫利用Jieba进行中文分词以及调用SnowNLP或Transformers库中的预训练模型进行情感预测的方法。2. 环境准备与工具说明在开始编码之前我们需要搭建好开发环境。本项目主要使用Python以下是核心库及其作用数据获取与处理requests: 用于发送HTTP请求获取网页HTML内容。BeautifulSoup4 (bs4): 用于解析HTML/XML文档提取所需的文本数据。Pandas: 数据处理和分析的核心库用于存储和清洗数据。中文文本处理Jieba: 优秀的中文分词工具能将句子切分成独立的词语。情感分析模型SnowNLP: 一个Python编写的中文文本处理库内置了基于贝叶斯算法训练的情感分析模型简单易用适合入门和快速验证。Transformers (by Hugging Face): 当前NLP领域的主流库提供了数千个预训练模型。我们将使用一个在中文情感分析任务上微调过的BERT模型以获得更准确的分析结果。数据可视化MatplotlibSeaborn: 用于绘制各种统计图表直观展示情感分布。环境配置步骤确保已安装Python建议3.8及以上版本。使用pip安装所需库。建议在虚拟环境中进行。# 创建并激活虚拟环境可选但推荐 # python -m venv nlp_env # source nlp_env/bin/activate # Linux/Mac # nlp_env\Scripts\activate # Windows # 安装核心库 pip install requests beautifulsoup4 pandas jieba snownlp # 安装Transformers及相关依赖PyTorch版本请根据自身CUDA情况选择 # 访问 https://pytorch.org/get-started/locally/ 获取适合你的安装命令 # 例如对于无GPU的机器 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu pip install transformers # 安装可视化库 pip install matplotlib seaborn版本说明本文代码基于上述库的常见稳定版本编写。由于开源库更新较快若遇到API变动请参考其官方文档进行调整。核心逻辑保持不变。3. 核心技术与原理浅析在深入代码之前理解背后的基本概念有助于我们更好地使用工具和解读结果。3.1 中文文本预处理流程与英文不同中文句子没有天然的空格分隔。因此分词是中文NLP的第一步。分词使用Jieba将句子“曼联是C罗梦想开始的地方”切分为[‘曼联’ ‘是’ ‘C罗’ ‘梦想’ ‘开始’ ‘的’ ‘地方’]。去停用词去除对情感分析贡献甚微的常见字词如“的”、“了”、“是”、“在”等。需要准备一个停用词表。文本清洗去除HTML标签、特殊符号、表情符号、无关链接等。3.2 情感分析模型简介我们将对比使用两种模型SnowNLP其情感分析模块基于朴素贝叶斯分类器在一个中文评论数据集上训练。它直接输出一个0到1之间的值越接近1表示越正面。优点是简单快速无需训练开箱即用。缺点是模型较旧可能无法很好地理解复杂的语境和网络新词。BERT (Transformers)BERT是一种基于Transformer架构的深度预训练语言模型。我们使用一个在中文情感分析任务上微调Fine-tuned过的BERT模型。它能更好地理解上下文语境准确率通常更高。缺点是计算资源消耗较大且需要一定的步骤来加载和使用。工作流程对比SnowNLP: 原始文本 - (可选分词) - SnowNLP().sentiments - 情感得分 (0-1) BERT: 原始文本 - Tokenizer (分词编码) - BERT模型 - 分类层 - 情感标签 (正面/负面)4. 完整实战构建曼联C罗情感分析系统接下来我们将分步骤实现整个系统。为了遵守法律法规和网站规范我们不会进行真实、大规模的网络爬取而是以模拟数据和从公开、允许爬取的测试页面获取少量示例的方式演示完整流程。在实际应用中你必须严格遵守目标网站的robots.txt协议并确保你的爬虫行为合法合规。4.1 数据采集模拟与示例我们模拟一个从新闻评论区假设结构采集数据的过程。# 文件data_collector.py import requests from bs4 import BeautifulSoup import pandas as pd import time import random def fetch_sample_data(): 模拟数据采集函数。 在实际项目中这里应替换为目标网站的真实URL和解析逻辑。 此处我们返回手工构造的示例数据并演示一次请求。 # 示例1模拟数据用于快速演示流程 sample_comments [ “C罗回归曼联梦剧场的孩子回家了泪目”, “这场踢得真臭全队梦游C罗也带不动。”, “曼联管理层真是业余耽误了C罗最后的巅峰。”, “虽然结果不好但看到C罗穿上红魔球衣就是青春。”, “滕哈格的处理方式有问题但C罗提前离场也不职业。”, “曼联现在的问题不是一个C罗能解决的。”, “永远的CR7无论在哪都支持你”, “走了好更衣室毒瘤终于清除了。”, “深夜看这些新闻真是‘梦啼妆泪红阑干’啊。”, “希望双方都好毕竟有过美好的回忆。” ] sample_dates [‘2021-08-27‘ ‘2022-04-23‘ ‘2022-10-20‘ ‘2022-01-15‘ ‘2022-10-22‘ ‘2022-11-23‘ ‘2022-11-23‘ ‘2022-11-24‘ ‘2022-11-25‘ ‘2022-11-26’] # 将模拟数据转换为DataFrame df pd.DataFrame({ ‘comment‘: sample_comments, ‘date‘: sample_dates }) # 示例2演示一次对公开测试页面的请求以百度为例仅作演示 print(“正在演示请求公开页面...“) try: # 这是一个公开的、允许访问的测试URL test_url ‘https://www.baidu.com‘ headers { ‘User-Agent‘: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36‘ } resp requests.get(test_url headersheaders timeout5) resp.raise_for_status() # 检查请求是否成功 print(f“请求成功状态码{resp.status_code}“) # 在实际爬取时这里会用BeautifulSoup解析resp.content # soup BeautifulSoup(resp.content ‘html.parser‘) # comments soup.find_all(‘div‘ class_‘comment-class‘) # 假设的类名 except requests.RequestException as e: print(f“网络请求示例出错正常仅作演示{e}“) # 重要在实际操作中必须遵守以下原则 # 1. 检查目标网站的 robots.txt (e.g., https://example.com/robots.txt)。 # 2. 设置合理的请求间隔如 time.sleep(random.uniform(1 3))避免对服务器造成压力。 # 3. 使用正确的User-Agent头部。 # 4. 仅采集公开可用且允许采集的数据。 return df if __name__ ‘__main__‘: data_df fetch_sample_data() print(“采集到的数据示例“) print(data_df.head()) # 保存到本地CSV文件供后续步骤使用 data_df.to_csv(‘manutd_cr7_comments.csv‘ indexFalse encoding‘utf-8-sig‘) print(“数据已保存至 ‘manutd_cr7_comments.csv‘“)4.2 数据清洗与预处理对采集到的原始文本进行清洗。# 文件data_preprocessor.py import pandas as pd import re import jieba from typing import List # 加载停用词表需自行准备一个中文停用词文件或使用网上公开的 def load_stopwords(stopwords_path: str ‘stopwords.txt‘) - set: try: with open(stopwords_path ‘r‘ encoding‘utf-8‘) as f: stopwords set([line.strip() for line in f]) except FileNotFoundError: print(f“警告未找到停用词文件 {stopwords_path}将使用内置简易列表。“) # 一个非常简易的停用词列表 stopwords set([‘的‘ ‘了‘ ‘在‘ ‘是‘ ‘我‘ ‘有‘ ‘和‘ ‘就‘ ‘不‘ ‘人‘ ‘都‘ ‘一‘ ‘一个‘ ‘上‘ ‘也‘ ‘很‘ ‘到‘ ‘说‘ ‘要‘ ‘去‘ ‘你‘ ‘会‘ ‘着‘ ‘没有‘ ‘看‘ ‘好‘ ‘自己‘ ‘这‘]) return stopwords def clean_text(text: str) - str: “”“基础文本清洗”“” if not isinstance(text str): return “” # 去除HTML标签如果爬取的文本含有 text re.sub(r‘[^]‘ ‘‘ text) # 去除URL链接 text re.sub(r‘https?://\S‘ ‘‘ text) # 去除提及和#话题# text re.sub(r‘[#]\S‘ ‘‘ text) # 去除特殊符号、数字、英文根据任务决定此处为保留可能的情感符号如‘‘‘‘ # 保留中文、汉字常见标点 text re.sub(r‘[^\u4e00-\u9fa5。“”‘’\s]‘ ‘‘ text) # 合并多余空白字符 text re.sub(r‘\s‘ ‘ ‘ text).strip() return text def segment_text(text: str stopwords: set) - List[str]: “”“中文分词并去除停用词”“” words jieba.lcut(text) # 去除停用词和单字可选 filtered_words [w for w in words if w not in stopwords and len(w) 1] return filtered_words def preprocess_data(input_path: str ‘manutd_cr7_comments.csv‘ output_path: str ‘processed_comments.csv‘): “”“主预处理函数”“” # 1. 加载数据 df pd.read_csv(input_path encoding‘utf-8-sig‘) print(f“原始数据量{len(df)}“) # 2. 加载停用词 stopwords_set load_stopwords() # 3. 文本清洗 df[‘cleaned_comment‘] df[‘comment‘].apply(clean_text) # 去除清洗后为空的行 df df[df[‘cleaned_comment‘].str.len() 0] print(f“清洗后数据量{len(df)}“) # 4. 分词 df[‘segmented_words‘] df[‘cleaned_comment‘].apply(lambda x: segment_text(x stopwords_set)) # 将分词列表合并为字符串SnowNLP需要字符串输入BERT需要列表或字符串均可 df[‘segmented_str‘] df[‘segmented_words‘].apply(lambda x: ‘ ‘.join(x)) # 5. 保存处理后的数据 df.to_csv(output_path indexFalse encoding‘utf-8-sig‘) print(f“预处理完成数据已保存至 {output_path}“) print(df[[‘comment‘ ‘cleaned_comment‘ ‘segmented_str‘]].head()) return df if __name__ ‘__main__‘: processed_df preprocess_data()4.3 使用SnowNLP进行情感分析SnowNLP使用最为简便。# 文件sentiment_snownlp.py import pandas as pd from snownlp import SnowNLP def analyze_with_snownlp(input_path: str ‘processed_comments.csv‘): df pd.read_csv(input_path encoding‘utf-8-sig‘) sentiments [] for text in df[‘cleaned_comment‘]: # 使用清洗后的完整句子 try: s SnowNLP(text) # sentiments属性返回0-1之间的值0.5一般为正面 score s.sentiments sentiments.append(score) except Exception as e: print(f“分析文本‘{text[:20]}...‘时出错{e}“) sentiments.append(0.5) # 出错时设为中性 df[‘snownlp_score‘] sentiments # 根据得分划分情感极性 df[‘snownlp_sentiment‘] df[‘snownlp_score‘].apply(lambda x: ‘正面‘ if x 0.6 else (‘负面‘ if x 0.4 else ‘中性‘)) print(“SnowNLP情感分析结果“) print(df[[‘comment‘ ‘snownlp_score‘ ‘snownlp_sentiment‘]].head(10)) # 统计分布 dist df[‘snownlp_sentiment‘].value_counts() print(f“\n情感分布\n{dist}“) df.to_csv(‘sentiment_results_snownlp.csv‘ indexFalse encoding‘utf-8-sig‘) return df if __name__ ‘__main__‘: snownlp_df analyze_with_snownlp()4.4 使用TransformersBERT进行情感分析这里我们使用Hugging Face Model Hub上一个开源的中文情感分析模型例如bert-base-chinese在某个情感数据集上微调的版本。请注意模型名称可能需要根据实际情况调整。# 文件sentiment_transformers.py import pandas as pd import torch from transformers import AutoTokenizer AutoModelForSequenceClassification import warnings warnings.filterwarnings(‘ignore‘) def analyze_with_bert(input_path: str ‘processed_comments.csv‘ model_name: str ‘uer/roberta-base-finetuned-jd-binary-chinese‘): “”“ 使用Hugging Face Transformers进行情感分析。 参数 model_name: 可以替换为其他中文情感分析模型例如 - ‘bert-base-chinese‘ (需自己定义分类头或找微调版) - ‘hfl/rbt3‘ - ‘nlptown/bert-base-multilingual-uncased-sentiment‘ (多语言) 这里选用一个在中文电商评论上微调的二分类模型作为示例。 “”“ df pd.read_csv(input_path encoding‘utf-8-sig‘) # 1. 加载模型和分词器 print(f“正在加载模型{model_name} ...“) tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForSequenceClassification.from_pretrained(model_name) # 将模型设置为评估模式 model.eval() # 2. 情感分析函数 def predict_sentiment(text): # 使用分词器处理文本 inputs tokenizer(text return_tensors“pt“ truncationTrue paddingTrue max_length128) # 推理不计算梯度 with torch.no_grad(): outputs model(**inputs) # 获取预测结果 predictions torch.nn.functional.softmax(outputs.logits dim-1) # 获取概率和类别 proba predicted_class torch.max(predictions dim1) # 假设模型输出0-负面 1-正面 (根据具体模型而定需查看其文档) # 这里我们根据概率判断 positive_score predictions[0][1].item() # 获取正面概率 # 根据模型实际情况调整映射 sentiment_label ‘正面‘ if predicted_class.item() 1 else ‘负面‘ return positive_score sentiment_label # 3. 对每条评论进行分析 bert_scores [] bert_sentiments [] for idx text in enumerate(df[‘cleaned_comment‘]): try: score label predict_sentiment(text) bert_scores.append(score) bert_sentiments.append(label) except Exception as e: print(f“分析第{idx}条文本时出错{e}“) bert_scores.append(0.5) bert_sentiments.append(‘中性‘) # 可选添加延迟避免过快请求对本地模型不是必须 # time.sleep(0.01) df[‘bert_score‘] bert_scores df[‘bert_sentiment‘] bert_sentiments print(“BERT情感分析结果“) print(df[[‘comment‘ ‘bert_score‘ ‘bert_sentiment‘]].head(10)) # 统计分布 dist df[‘bert_sentiment‘].value_counts() print(f“\n情感分布\n{dist}“) df.to_csv(‘sentiment_results_bert.csv‘ indexFalse encoding‘utf-8-sig‘) return df if __name__ ‘__main__‘: # 注意首次运行会下载模型可能需要较长时间和网络环境支持。 bert_df analyze_with_bert()4.5 结果可视化与分析将两种方法的结果进行对比和可视化。# 文件visualize_sentiment.py import pandas as pd import matplotlib.pyplot as plt import seaborn as sns def visualize_results(snownlp_path: str ‘sentiment_results_snownlp.csv‘ bert_path: str ‘sentiment_results_bert.csv‘): # 合并两个结果文件假设它们行序一致 df_snownlp pd.read_csv(snownlp_path encoding‘utf-8-sig‘) df_bert pd.read_csv(bert_path encoding‘utf-8-sig‘) # 确保评论列一致然后合并情感结果 comparison_df df_snownlp[[‘comment‘ ‘date‘ ‘snownlp_sentiment‘]].copy() comparison_df[‘bert_sentiment‘] df_bert[‘bert_sentiment‘] # 1. 绘制两种方法的情感分布对比柱状图 fig axes plt.subplots(1 2 figsize(12 5)) snownlp_dist comparison_df[‘snownlp_sentiment‘].value_counts() bert_dist comparison_df[‘bert_sentiment‘].value_counts() axes[0].bar(snownlp_dist.index snownlp_dist.values color[‘lightcoral‘ ‘lightblue‘ ‘lightgreen‘]) axes[0].set_title(‘SnowNLP 情感分布‘) axes[0].set_ylabel(‘评论数量‘) axes[1].bar(bert_dist.index bert_dist.values color[‘lightcoral‘ ‘lightblue‘]) axes[1].set_title(‘BERT 情感分布‘) axes[1].set_ylabel(‘评论数量‘) plt.tight_layout() plt.savefig(‘sentiment_distribution_comparison.png‘ dpi300) plt.show() # 2. 情感随时间的变化趋势假设有日期数据 if ‘date‘ in comparison_df.columns: comparison_df[‘date‘] pd.to_datetime(comparison_df[‘date‘]) # 按日期和SnowNLP情感分组计数 trend_snownlp comparison_df.groupby([‘date‘ ‘snownlp_sentiment‘]).size().unstack(fill_value0) trend_snownlp.plot(kind‘line‘ marker‘o‘ figsize(10 6)) plt.title(‘SnowNLP情感倾向随时间变化‘) plt.ylabel(‘评论数量‘) plt.xlabel(‘日期‘) plt.grid(True linestyle‘--‘ alpha0.7) plt.tight_layout() plt.savefig(‘sentiment_trend_snownlp.png‘ dpi300) plt.show() # 3. 查看两种模型结果不一致的样本 disagreement comparison_df[comparison_df[‘snownlp_sentiment‘] ! comparison_df[‘bert_sentiment‘]] print(f“两种模型分析结果不一致的样本数{len(disagreement)}“) if not disagreement.empty: print(“不一致样本示例“) print(disagreement[[‘comment‘ ‘snownlp_sentiment‘ ‘bert_sentiment‘]].head()) # 可以进一步分析这些样本理解模型的差异 return comparison_df if __name__ ‘__main__‘: result_df visualize_results()运行上述代码后你将得到情感分布的对比图并能直观地看到SnowNLP和BERT模型在分析“曼联C罗”相关评论时的差异。5. 常见问题与排查思路在实现上述流程时你可能会遇到以下问题问题现象可能原因解决思路requests爬虫被网站屏蔽或返回403错误。1. 未设置User-Agent头部。2. 请求频率过高触发反爬。3. 网站需要登录或验证。1. 在请求头中添加合理的User-Agent。2. 在请求间添加随机延时time.sleep。3.遵守robots.txt评估爬取行为的合法性。Transformers下载模型失败或速度极慢。网络连接问题或无法访问Hugging Face模型仓库。1. 配置网络代理需确保合法合规。2. 使用国内镜像源如魔搭社区 ModelScope。3. 提前下载模型文件到本地从本地路径加载。BERT模型预测结果全部相同或非常奇怪。1. 模型不适合当前任务如用英文模型分析中文。2. 文本预处理方式与模型训练时不符。3. 未正确处理模型的输入输出。1. 确认模型是否针对中文情感分析进行过微调。2. 查看模型卡Model Card了解其预期的输入格式是否需特殊分词、加特殊标记。3. 检查tokenizer和model的调用方式确保softmax应用正确。SnowNLP情感得分集中在0.5附近区分度低。SnowNLP内置模型训练数据较旧对网络用语、特定领域如体育评论不敏感。1. 调整情感划分阈值如以0.55和0.45为界。2. 考虑使用SnowNLP训练自定义情感分类器需标注数据。3. 直接使用更先进的预训练模型如BERT。分词效果差重要词语被切碎。Jieba默认词典未收录领域新词如“滕哈格”、“梦剧场”。1. 使用jieba.add_word(“滕哈格”)添加自定义词典。2. 加载更大的用户自定义词典文件。程序运行内存不足OOM。BERT模型尤其是base或large版本在处理长文本或批量数据时消耗大量内存。1. 减少max_length参数。2. 使用更小的模型如tinysmall。3. 逐条或小批量batch处理数据而非一次性加载所有文本。6. 最佳实践与工程建议将情感分析系统投入实际应用或进行更深入的研究时以下几点建议可以帮助你提升效果和稳健性数据质量是根本爬虫伦理与合规始终优先考虑使用公开的数据集。若必须爬取务必尊重网站规则控制爬取频率并明确标注数据来源。清洗策略定制化针对体育评论可能需要保留一些特殊符号如“”可能表示强烈情绪或专门处理球迷黑话、昵称。清洗规则需要根据数据特点反复调整。模型选择与评估不要迷信单一模型如本项目所示SnowNLP和BERT的结果可能不同。对于关键分析可以结合多个模型的结果或采用集成方法。进行人工评估随机抽取一部分数据进行人工情感标注计算模型预测准确率、精确率、召回率等指标。这是衡量模型在你的特定数据上表现的金标准。考虑领域适配通用情感模型在体育、电竞、财经等专业领域可能表现不佳。如果数据量和资源允许使用标注数据对预训练模型进行微调是提升效果最有效的方法。工程化部署考虑API服务化将情感分析模型封装为RESTful API使用FastAPI、Flask等方便其他系统调用。性能优化对于BERT类模型可以使用ONNX Runtime或TensorRT进行推理加速或使用专为推理优化的库如DeepSpeed。缓存机制对相同的文本进行重复分析时可以使用缓存如Redis存储结果提升响应速度。深入分析与洞察主题与情感结合使用LDA等主题模型先识别评论中讨论的子话题如“管理层”、“战术”、“薪资”、“情怀”再分析每个话题下的情感分布洞察更深刻。时间序列分析如本项目可视化部分所示将情感与时间维度结合可以清晰看到关键事件官宣、比赛、采访如何影响公众情绪。跨平台对比分析不同平台如微博、虎扑、懂球帝对同一事件的情感倾向差异可以了解不同社区的舆论场特征。通过以上步骤我们不仅完成了一个针对“曼联C罗”情感分析的技术Demo更掌握了一套可复用于其他中文文本情感分析场景的方法论。从数据获取到模型应用再到结果可视化与问题排查这套流程具有很强的通用性。技术是冰冷的但数据背后的情感是鲜活的。用代码去量化、分析这种情感正是数据科学的魅力之一。