用Python+NRC词典解码微博情绪:从数据清洗到多维可视化的实战指南
最近在分析一个社交媒体项目时,我遇到了一个挺有意思的挑战:如何从海量的微博评论中,不只是简单地判断正面或负面,而是精准地捕捉用户那些更细腻的情绪波动——比如愤怒、惊喜、期待,甚至是厌恶。传统的二分类情感分析在这里显得有些力不从心,直到我开始深入研究NRC情感词典,才发现原来情绪可以被如此精细地量化。
对于数据分析师和Python开发者来说,社交媒体上的文本分析早已不是新鲜事。但真正把分析做到“有温度”,能区分出用户是“愤怒地吐槽”还是“悲伤地诉说”,这中间的差别往往决定了商业洞察的深度。NRC词典提供的正是这样一套多维度的情绪坐标系,它把情感拆解成8种基本情绪和正负两极,让我们能够用数据语言描述人类的复杂情感。
在这篇文章里,我会分享一套完整的实战流程,从原始微博数据的抓取清洗开始,到NRC词典的本地化处理,再到避免中文分词陷阱的技巧,最后用多种可视化方式呈现情绪权重分布。整个过程都是我在实际项目中踩过坑、优化过的方案,特别适合需要处理中文社交媒体数据的朋友。
1. 环境准备与数据获取
1.1 Python环境配置
开始之前,确保你的Python环境已经安装了必要的库。我推荐使用Anaconda创建独立的环境,避免版本冲突。
# 创建新的conda环境
conda create -n weibo_emotion python=3.9
conda activate weibo_emotion
# 安装核心依赖
pip install pandas numpy jieba
pip install matplotlib seaborn plotly
pip install requests beautifulsoup4
pip install openpyxl # 用于读取Excel格式的NRC词典
如果你习惯用pipenv或者virtualenv,原理也是一样的。关键是要有pandas做数据处理,jieba做中文分词,再加上matplotlib或seaborn做可视化。
注意:NRC词典的官方文件是Excel格式,所以openpyxl是必须的。如果你下载的是CSV版本,可以跳过这个安装。
1.2 NRC词典的获取与初步探索
NRC情感词典的官方名称是“NRC Emotion Lexicon”,由加拿大国家研究委员会开发。它覆盖了105种语言,包括简体中文。词典的独特之处在于,每个词语都被标注了是否属于8种基本情绪(愤怒、期待、厌恶、恐惧、喜悦、悲伤、惊喜、信任)以及正负情感倾向。
你可以从NRC的官方网站或学术数据平台获取最新版本。下载后,先用pandas看看它的结构:
import pandas as pd
# 加载词典文件
lexicon_path = 'NRC-Emotion-Lexicon-v0.92-In105Languages-Nov2017Translations.xlsx'
lexicon_df = pd.read_excel(lexicon_path)
# 查看列名和数据结构
print("词典总列数:", len(lexicon_df.columns))
print("前5列名称:", lexicon_df.columns[:5].tolist())
# 提取中文部分
chinese_columns = ['Chinese (Simplified) (zh-CN)', 'Positive', 'Negative',
'Anger', 'Anticipation', 'Disgust', 'Fear',
'Joy', 'Sadness', 'Surprise', 'Trust']
chinese_df = lexicon_df[chinese_columns].copy()
print(f"中文词典条目数: {len(chinese_df)}")
print("\n前几条数据示例:")
print(chinese_df.head())
运行这段代码,你会看到一个结构清晰的DataFrame。每一行代表一个中文词语,后面的列用1或0标记这个词语是否属于某种情感类别。比如“快乐”这个词,在“Joy”和“Positive”列会是1,其他列是0。
1.3 微博评论数据获取策略
对于微博数据,我通常采用两种方式获取:
方式一:微博官方API 如果你有企业资质或研究用途,可以申请微博开放平台的API权限。这种方式最稳定合规,但需要审核。
方式二:网页爬取(仅供学习研究) 对于小规模分析,可以通过requests和BeautifulSoup获取公开数据。这里强调一下,一定要遵守网站的robots.txt,控制请求频率,避免对服务器造成压力。
import requests
from bs4 import BeautifulSoup
import time
import random
def fetch_weibo_comments(weibo_id, max_comments=100):
"""模拟获取微博评论(示例函数,实际需要根据微博页面结构调整)"""
base_url = f"https://weibo.com/ajax/statuses/buildComments"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': f'https://weibo.com/{weibo_id}'
}
comments = []
page = 1
while len(comments) < max_comments:
params = {
'id': weibo_id,
'page': page,
'count': 20
}
try:
response = requests.get(base_url, headers=headers, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
if 'data' in data and data['data']:
for item in data['data']:
comment_text = item.get('text', '').strip()
if comment_text:
comments.append({
'comment_id': item.get('id'),
'text': comment_text,
'user': item.get('user', {}).get('screen_name', ''),
'created_at': item.get('created_at', '')
})
page += 1
# 随机延迟,避免请求过快
time.sleep(random.uniform(1, 3))
else:
break
else:
print(f"请求失败,状态码: {response.status_code}")
break
except Exception as e:
print(f"获取数据时出错: {e}")
break
return pd.DataFrame(comments)
提示:在实际项目中,我建议将获取的数据立即保存到本地文件或数据库,避免重复请求。同时,一定要添加异常处理和日志记录,方便调试。
2. 数据清洗与预处理:处理中文社交媒体的特殊性
2.1 微博文本的典型噪声
微博评论和正式文本有很大不同,直接分析效果会很差。常见的噪声包括:
- 表情符号和颜文字:😂、😭、T_T、QAQ
- 话题标签和@用户:#今日心情#、@好友名字
- URL链接和短链:http://t.cn/xxxxx
- 重复字符和网络用语:“哈哈哈哈哈哈”、“yyds”、“绝绝子”
- 中英文混合及拼音:“这个idea不错”、“xswl”(笑死我了)
处理这些噪声需要一套组合拳。我常用的清洗流程是这样的:
import re
import jieba
from zhon.hanzi import punctuation as chinese_punctuation
class WeiboTextCleaner:
def __init__(self):
# 加载停用词表
self.stopwords = self._load_stopwords()
# 编译常用正则表达式
self.url_pattern = re.compile(r'https?://\S+|www\.\S+')
self.mention_pattern = re.compile(r'@\w+')
self.hashtag_pattern = re.compile(r'#\w+#')
self.emoji_pattern = re.compile("["
u"\U0001F600-\U0001F64F" # 表情符号
u"\U0001F300-\U0001F5FF" # 符号和象形文字
u"\U0001F680-\U0001F6FF" # 交通和地图符号
u"\U0001F1E0-\U0001F1FF" # 国旗符号
"]+", flags=re.UNICODE)
def _load_stopwords(self):
"""加载中文停用词表"""
stopwords = set()
# 可以从文件加载,这里先定义一些基础停用词
basic_stops = ['的', '了', '在', '是', '我', '有', '和', '就',
'不', '人', '都', '一', '一个', '上', '也', '很',
'到', '说', '要', '去', '你', '会', '着', '没有',
'看', '好', '自己', '这']
stopwords.update(basic_stops)
return stopwords
def clean_text(self, text):
"""完整的文本清洗流程"""
if not isinstance(text, str):
return ""
# 1. 转换为小写(英文部分)
text = text.lower()
# 2. 移除URL
text = self.url_pattern.sub('', text)
# 3. 移除@提及和话题标签
text = self.mention_pattern.sub('', text)
text = self.hashtag_pattern.sub('', text)
# 4. 移除表情符号
text = self.emoji_pattern.sub('', text)
# 5. 处理重复字符(如"哈哈哈哈" -> "哈")
text = re.sub(r'(.)\1{2,}', r'\1', text)
# 6. 移除多余空白字符
text = re.sub(r'\s+', ' ', text).strip()
return text
def tokenize_chinese(self, text):
"""中文分词并去除停用词"""
cleaned_text = self.clean_text(text)
if not cleaned_text:
return []
# 使用jieba分词
words = jieba.lcut(cleaned_text)
# 过滤停用词和标点
filtered_words = []
for word in words:
word = word.strip()
if (word and
word not in self.stopwords and
word not in chinese_punctuation and
not word.isspace() and
len(word) > 1): # 过滤单字(除非是情感词)
filtered_words.append(word)
return filtered_words
2.2 处理网络新词与情感词扩展
中文网络用语更新极快,很多新词在标准词典里找不到。比如"破防了"、"emo了"、"蚌埠住了"这些词,都有强烈的情感色彩,但NRC词典可能没有收录。
我的做法是维护一个自定义的情感词扩展表:
# 自定义情感词扩展(示例)
custom_emotion_words = {
'破防了': {'Anger': 0.7, 'Sadness': 0.8, 'Negative': 1},
'emo了': {'Sadness': 0.9, 'Negative': 1},
'蚌埠住了': {'Joy': 0.6, 'Surprise': 0.5, 'Positive': 0.7},
'yyds': {'Joy': 0.8, 'Trust': 0.7, 'Positive': 1},
'绝绝子': {'Joy': 0.7, 'Surprise': 0.6, 'Positive': 0.8},
'躺平': {'Sadness': 0.6, 'Negative': 0.7, 'Disgust': 0.4},
'内卷': {'Fear': 0.5, 'Negative': 0.8, 'Anger': 0.3},
}
# 可以定期从社交媒体热点中更新这个词表
def update_custom_lexicon(new_words_dict):
"""更新自定义词典"""
global custom_emotion_words
for word, emotions in new_words_dict.items():
if word not in custom_emotion_words:


899

被折叠的 条评论
为什么被折叠?



