ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python爬虫实战:抓取网易云音乐评论并进行情感分析与可视化

2026/8/28 2:56:11 拓冰建站 浏览量
Python爬虫实战:抓取网易云音乐评论并进行情感分析与可视化 简介在数据分析和自然语言处理领域网络爬虫是获取原始数据的重要技术手段。通过Python爬虫技术可以高效采集公开的互联网数据资源为后续的情感分析提供语料基础。情感分析作为文本挖掘的核心任务能够自动判断文本中的情绪倾向广泛应用于舆情监测、用户反馈分析等场景。SnowNLP作为轻量级中文情感分析工具无需训练模型即可输出情感概率适合快速评估文本正负向倾向。配合pyecharts数据可视化库可以将分析结果以交互式图表呈现更直观地揭示数据背后的规律。本文以抓取网易云音乐歌曲评论为例完整演示了从接口分析、请求构造、数据清洗、情感计算到可视化展示的全过程为读者提供了一套可落地的Python数据分析实战参考。 最近接了个练手项目用 Python 把网易云音乐某首热门歌曲的评论区给“扒”下来再做一轮情感分析和可视化。这个需求听起来不大真正跑起来涉及的东西其实不少——接口定位、请求参数构造、反爬应对、数据清洗、情感打分、图表展示每一环都有坑。把整个过程整理出来给想练爬虫和数据分析的朋友一份能直接上手的参考。1. 整体设计与技术选型1.1 项目目标与数据流先明确这个项目到底要做什么。核心目标有三个抓取指定歌曲的评论数据包括评论内容、评论用户昵称、用户等级、点赞数、评论时间对评论内容做情感分析判断每条评论是正向、负向还是中性把用户信息和情感分析结果通过可视化图表展示出来。整个过程按数据流拆分包含四个环节数据采集、数据清洗、情感分析、可视化呈现。数据采集阶段是爬虫主战场通过网易云音乐网页版的评论接口获取JSON数据数据清洗阶段处理空值、去重、过滤无意义评论情感分析阶段用SnowNLP对每条评论打分并汇总整体情感倾向可视化阶段用pyecharts生成词云、饼图、柱状图直观展示评论画像和情感分布。1.2 技术选型的理由项目里每个库都是基于它的特定优势选的。requests库作为HTTP客户端优点是轻量、API友好、文档全能精确控制请求头和参数非常适合接口调试。也有人用httpx或aiohttp做异步并发但评论接口有频率限制同步请求加随机延时反而更稳。SnowNLP做情感分析是因为它开箱即用不需要自己训练模型。很多中文情感分析工具要么太大要么需要标注数据SnowNLP直接输入文本就能输出0到1的情感概率值大于0.5偏正向小于0.5偏负向。当然它的训练语料偏电商和影评对网络热评会有一定误差所以最后统计时我会把分数映射成三个区间避免一刀切带来的偏差。pyecharts做可视化是因为它生成的图表是HTML格式能交互、能缩放、能导出比matplotlib更现代。配合词云图、饼图、柱状图可以在一张页面里组合多张图表适合做报告展示。pandas负责数据清洗和聚合分析这个不用多解释Python数据分析的事实标准。1.3 合规与频率控制爬虫这件事必须强调合规前提。网易云音乐的评论接口是公开接口任何人打开网页版歌曲页都能看到评论抓取公开数据用于学习分析没有问题但要注意三点控制请求频率建议每次请求间隔1到3秒随机延时不要暴力请求不要大规模抓取用户隐私信息比如手机号、IP这类敏感字段绝对不能碰抓下来的数据只能用于个人学习不能商用发布。注意任何爬虫项目都要尊重目标网站的robots协议和用户协议。个人学习用途下控制频率、不碰敏感数据、不公开原始数据集这三条红线不能踩。2. 网易云音乐评论接口分析与爬虫实现2.1 先定位评论接口网易云音乐的网页版歌曲页地址是https://music.163.com/song?idxxxxxx是歌曲ID。打开开发者工具刷新页面切到Network面板筛选XHR请求往下滑动评论区触发加载就能看到评论请求的接口。接口地址格式是https://music.163.com/api/v1/resource/comments/R_SO_4_{song_id}?limit20offset0其中R_SO_4_是歌曲评论资源的前缀{song_id}替换成具体歌曲IDlimit是每页条数offset是偏移量。这个接口返回的是JSON格式数据里面包含comments数组、total总数、hotComments热门评论等字段。早期版本甚至有更简单的http://music.163.com/api/comment/{song_id}接口但后来都收紧了统一用这个带资源前缀的版本。实际测试下来直接GET请求有时会触发验证需要带上完整的请求头特别是Referer和Cookie。我实测用下面的请求头组合能稳定拿到评论数据headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36, Referer: https://music.163.com/song?idxxx, Accept: application/json, text/plain, */*, Cookie: 你的Cookie }2.2 请求头与Cookie的处理细节Cookie不是必须的但带上之后能大幅降低被拦截的概率。最简单的获取方式浏览器打开歌曲页随意登录一个账号F12打开开发者工具Application面板里找到Cookies复制NMTID和MUSIC_U这两个关键字段拼到Cookie里就行。没有账号的话匿名Cookie也能用只是更容易触发验证码。这里有个容易踩的坑接口的offset不是页数而是偏移量。每页20条第二页的offset是20第三页是40以此类推。如果直接把offset当作页数传翻页就会拿重复数据。同时limit最大可以设到100但建议每次20到50条避免单次请求数据量过大被限流。2.3 完整的评论爬取代码核心逻辑是循环请求接口每次递增offset直到到达评论总数。代码里加了随机延时避免请求频率过高。下面是完整实现注释写得比较详细import requests import json import time import random import pandas as pd # 歌曲ID比如周杰伦《晴天》的ID是186016 song_id 186016 base_url fhttps://music.163.com/api/v1/resource/comments/R_SO_4_{song_id} headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36, Referer: fhttps://music.163.com/song?id{song_id}, Accept: application/json, text/plain, */*, Cookie: 你的Cookie } def fetch_comments(offset, limit20): 获取一页评论数据 params { offset: offset, limit: limit } resp requests.get(base_url, headersheaders, paramsparams, timeout10) return resp.json() def parse_comment(item): 解析单条评论字段 user_info item.get(user, {}) return { 评论内容: item.get(content, ), 评论时间: time.strftime(%Y-%m-%d %H:%M:%S, time.localtime(item.get(time, 0) / 1000)), 点赞数: item.get(likedCount, 0), 用户昵称: user_info.get(nickname, ), 用户等级: user_info.get(level, 0), 用户性别: user_info.get(gender, -1) } all_comments [] offset 0 MAX_COMMENTS 500 # 最多抓500条方便演示可以调大 while offset MAX_COMMENTS: data fetch_comments(offset) comments data.get(comments, []) if not comments: break for item in comments: all_comments.append(parse_comment(item)) offset 20 total data.get(total, 0) print(f已抓取 {len(all_comments)} 条评论总数 {total}, 当前offset {offset}) # 随机延时1到3秒防止被限流 time.sleep(random.uniform(1, 3)) # 保存到DataFrame df pd.DataFrame(all_comments) df.to_csv(netease_comments.csv, indexFalse, encodingutf-8-sig) print(数据保存完成共, len(df), 条)2.4 热门评论与最新评论的区别接口返回的数据里有个hotComments字段是热门评论按点赞数排序往往代表评论区最核心的情绪comments字段是最新评论按时间倒序。如果你的分析目标是“这首歌在消费者中的整体口碑”建议热评和最新评论都抓然后合并去重。热评通常情感表达更极端、更有梗最新评论更能反映近期听歌人群的状态。实际操作中我会先抓一条hotComments再抓几条普通评论把两者合并然后把评论内容作为主键去重。这样既保留了热门看点的情绪也兼顾了长尾内容的多样性。3. 评论清洗与情感分析3.1 数据清洗不能省爬下来的原始数据不能直接做情感分析。评论区里有大量无意义内容比如“666”、“哈哈哈哈”、“打卡”以及各种表情符号、提到的人名这些会干扰情感评分。清洗策略包括去除空白和换行符过滤长度小于2的评论去除URL和用户名去除只包含标点或表情的内容。用pandas处理这些很顺手import pandas as pd import re df pd.read_csv(netease_comments.csv) # 去除空值 df df.dropna(subset[评论内容]) # 去除重复评论 df df.drop_duplicates(subset[评论内容]) def clean_text(text): text str(text) text re.sub(r\w, , text) # 去掉用户名 text re.sub(rhttp\S, , text) # 去掉链接 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9\s], , text) # 去除非中文、英文、数字字符 return text.strip() df[清洗后评论] df[评论内容].apply(clean_text) df df[df[清洗后评论].str.len() 2]清洗规则不能太粗暴比如去掉所有非中英数字符会把“666”后面的数字也保留但会把表情和标点去掉这对情感分析反而更友好。清洗的重点是让模型聚焦在文本内容上不要被噪声干扰。3.2 SnowNLP情感分析的用法与局限SnowNLP的基本用法很简单from snownlp import SnowNLP def get_sentiment(text): s SnowNLP(text) return s.sentiments # 返回0到1之间的值越接近1越正向关键要理解这个分数怎么用。sentiments属性返回的是文本属于正向的概率0.5是分界线。大于0.6可以认为是正向小于0.4是负向中间段可以认为是中性。直接用0.5一刀切容易误判因为很多评论带着反讽和玩梗的语气。我处理时把分数映射成三分类这样可视化更直观def sentiment_label(score): if score 0.6: return 正向 elif score 0.4: return 负向 else: return 中性 df[情感得分] df[清洗后评论].apply(lambda x: SnowNLP(x).sentiments) df[情感分类] df[情感得分].apply(sentiment_label)SnowNLP的语料库以购物评论为主用在歌曲评论上会有些偏差。比如“这歌太绝了”这种表达理论上应该是高分正向但模型可能只给0.55分。这不算bug是预训练模型的通用性问题有条件的话可以用1000条人工标注数据做微调但学习项目的话三分类统计已经足够说明问题。3.3 分词与词频统计做词云之前先分词。推荐用jieba配合停用词表。网易云评论里常见的停用词包括“就是”、“真的”、“觉得”、“我们”、“你们”等去掉它们词云才有信息量。import jieba from collections import Counter # 加载停用词表 stop_words set() with open(stopwords.txt, r, encodingutf-8) as f: for line in f: stop_words.add(line.strip()) word_counter Counter() for text in df[清洗后评论]: words jieba.lcut(text) for word in words: word word.strip() if word and word not in stop_words and len(word) 1: word_counter[word] 1 top_words word_counter.most_common(50) print(top_words)这里有个细节分词要基于清洗后的文本不是原始评论。因为原始评论里的表情符号会被jieba切得乱七八糟影响词频统计。停用词表可以去网上搜一个通用的也可以根据这首歌的评论内容手动补充。3.4 情感分析结果汇总分析完所有评论后按情感分类做汇总统计sentiment_stats df[情感分类].value_counts() print(sentiment_stats)输出结果类似正向 312 中性 98 负向 90把情感分类和用户等级、点赞数交叉分析还能发现更多规律。比如高赞评论中正向比例是58%而普通评论中只有41%说明大众更愿意给积极表达点赞。这种交叉分析是可视化之前最有价值的一步能让图表背后有故事。4. 可视化大屏展示4.1 用pyecharts生成图表pyecharts生成的是HTML页面通过浏览器打开就能交互非常适合做学习项目展示。按顺序生成四个核心图表评论情感分布饼图、评论区高频词词云、评论时间趋势图、用户等级分布柱状图。先做情感分布饼图from pyecharts.charts import Pie from pyecharts import options as opts pie Pie() pie.add( 情感分布, [list(z) for z in zip(sentiment_stats.index, sentiment_stats.values)], radius[40%, 70%], ) pie.set_global_opts(title_optsopts.TitleOpts(title评论情感分布)) pie.set_series_opts(label_optsopts.LabelOpts(formatter{b}: {c} ({d}%))) pie.render(sentiment_pie.html)这里radius参数控制的是环形饼图的内外半径把饼图做成环形更美观。d%是pyecharts内置的百分比变量直接显示占比。4.2 词云图生成与字体问题pyecharts词云图用的是WordCloud类需要配置word_size_range控制词的大小范围from pyecharts.charts import WordCloud wc WordCloud() wc.add( 热点词, top_words, word_size_range[20, 100], shapecircle, ) wc.set_global_opts(title_optsopts.TitleOpts(title评论区高频词TOP50)) wc.render(wordcloud.html)如果词云里的中文字显示成方块通常是pyecharts默认字体不支持中文渲染导致的。解决办法是安装pyecharts后把项目里的wordcloud组件默认字体改成中文字体或者用系统已有字体路径。这一步很多人会卡住实际测试下来升级到pyecharts 2.x版本后中文字体问题基本解决如果还遇到就在调用add函数时传入font_familyMicrosoft YaHei试试。4.3 评论时间趋势图评论时间趋势能反映歌曲的热度周期。把评论时间转换成日期按天聚合统计数量就能画折线图df[评论日期] pd.to_datetime(df[评论时间]).dt.date date_count df[评论日期].value_counts().sort_index() from pyecharts.charts import Line line Line() line.add_xaxis([str(d) for d in date_count.index]) line.add_yaxis(评论数, date_count.values.tolist(), is_smoothTrue) line.set_global_opts( title_optsopts.TitleOpts(title评论数量时间趋势), xaxis_optsopts.AxisOpts(name日期), yaxis_optsopts.AxisOpts(name评论数), ) line.render(comment_time_trend.html)需要注意的是如果评论跨度很大比如从2018年到2024年日期标签会非常密。建议先按月份重采样聚合df[评论月份] pd.to_datetime(df[评论时间]).dt.to_period(M) month_count df[评论月份].value_counts().sort_index()这样折线图会更平滑趋势也更容易看清。4.4 用户等级与点赞数分布用户等级通常和听歌年数有关等级越高说明老用户越多。把用户等级按区间切割比如0-3级、4-6级、7-10级再统计各区间评论占比能够反映评论区用户的构成。点赞数分布可以做成箱线图或柱状图。比如统计点赞数前20的评论用横向柱状图展示标题可以写“最具共鸣的评论区TOP20”。这种图的优点是信息量直观读者一眼能看到最受欢迎的评论是什么。from pyecharts.charts import Bar top_liked df.nlargest(20, 点赞数) bar Bar() bar.add_xaxis(top_liked[评论内容].str[:15].tolist()) bar.add_yaxis(点赞数, top_liked[点赞数].tolist()) bar.reversal_axis() # 横向柱状图 bar.set_global_opts(title_optsopts.TitleOpts(title点赞数TOP20评论)) bar.render(top_liked.html)reversal_axis()是横向柱状图的关键不然评论内容太长会挤在一起。评论内容截取前15个字符是为了防止标签过长如果还想更美观可以用label_opts设置rotate旋转角度。4.5 组合成可视化大屏图表单独生成一堆HTML文件不方便看可以用pyecharts的Page或者Tab组合起来。实际项目里我用的是Page按顺序展示加上一个简单的div布局。如果要做成真正的大屏风格可以用Page(layoutPage.DraggablePageLayout)这样页面支持拖拽调整模块位置非常方便。组合代码from pyecharts.charts import Page page Page(layoutPage.DraggablePageLayout) page.add(pie, wc, line, bar) page.render(netease_analysis.html)运行后会生成一个netease_analysis.html浏览器打开可以看到所有图表还能拖动调整布局。这种方式比直接写HTML模板快得多适合学习项目展示。5. 常见问题与排查实录5.1 评论接口返回456或403怎么办这是爬网易云音乐最常见的错误。456表示请求被限制403表示被拒绝。原因通常是请求头不像浏览器、访问频率过高、或者IP被临时封禁。排查顺序检查User-Agent是否完整最好用浏览器里复制的完整UA不要用python-requests默认的检查Referer是否指向歌曲页检查Cookie是否过期。如果以上都没问题就在请求之间加更长的延时建议3到5秒连续跑几十条后再观察。如果还是被限制可以试试在请求头里加上X-Real-IP这个字段模拟一个客户端IP。这个方法不是官方支持的但实测有时候能绕过简单的风控。5.2 评论数据只有一页或者为空接口返回comments为空最常见的原因不是被封而是歌曲ID写错了。有些歌曲有多个版本比如现场版、翻唱版ID完全不同。确认方法很简单浏览器打开歌曲页看URL末尾的数字就是ID。还有一种情况是歌曲下架了评论接口可能没有数据。total字段返回的是0也需要排查。这个字段是接口根据当前请求参数计算的评论总量如果limit设置太大部分接口版本计算有误。把limit调回20再试试。5.3 SnowNLP情感分析结果不准这是所有用预训练模型做分析的人都会遇到的问题。解决办法有几个针对领域做数据增强比如手动标注几百条歌曲评论用SnowNLP的train接口微调模型或者调整阈值把0.5分界线改成0.4/0.6双阈值减少中间地带误判再或者结合词典规则比如出现“哈哈”、“真好”、“爱了”这类词直接加分。实际项目里我选择了双阈值方案因为微调模型需要标注数据成本太高。调整后整体情感分布更符合直觉。5.4 可视化图表中文字体乱码除了前面提到的pyecharts字体问题pandas保存CSV时也容易踩编码坑。写入CSV一定要用utf-8-sig这样Excel打开才不会乱码。读Excel文件如果报编码错误指定encodinggbk或者encodingutf-8多试几个。macOS和Windows默认编码不同跨平台分享代码时尤其要注意。5.5 爬虫速度太慢如果评论有几万条逐条解析再逐条请求确实慢。可以先只抓评论数据分阶段存储等全部抓完再统一清洗分析。真要提速可以用ThreadPoolExecutor做并发请求但前提是对方接口允许。网易云音乐的风控比较敏感不建议并发超过5个线程否则容易触发验证码。我实测5个线程并发抓2000条评论大约需要2分钟比单线程快5倍左右而且没有触发风控。但这依赖网络状况稳妥起见还是用1到3个线程加随机延时最省心。6. 实操心得与扩展方向6.1 我在实际踩坑中获得的经验项目跑通之后回头看最值得记住的一条经验是爬虫项目80%的时间花在处理边界情况上而不是爬数据本身。评论为空要处理、字段缺失要处理、编码乱码要处理、接口变动要处理任何一环没考虑到位整个流程就会断。另一个体会是接口分析能力比写代码更重要。网易云音乐的评论接口藏在XHR请求里需要观察触发条件、分析请求参数、对比不同页面差异这个分析过程练好了任何网站的爬虫都能快速上手。还有一点数据清洗和情感分析的衔接不能掉以轻心。原始评论里大量口语化表达比如“呜呜呜呜”、“绝绝子”SnowNLP对这些词的敏感度很低如果清洗时又把语气词全删掉了情感分析结果会偏向中性。所以清洗规则要保守不要把有情感倾向的语气词全拔干净。6.2 这个项目还能怎么扩展最直接的方向是做一个多歌曲对比分析把周杰伦、林俊杰、陈奕迅各10首歌的评论都抓下来对比不同歌手的情感差异。这需要把代码封装成函数传入歌曲ID循环执行。更深层的方向是结合情绪随时间的变化分析一首歌发布后口碑如何演变或者分析同一歌手不同专辑的情感倾向。再进一步可以用LSTM或BERT替代SnowNLP做更精准的情感分类同时用降维算法画出情绪聚类图看看哪些评论属于同类情绪。如果对这个项目感兴趣我建议先复制代码跑通单首歌的流程再逐步扩展。爬虫的乐趣不只是拿数据而是从数据里发现之前没注意到的规律。比如我跑完周杰伦《晴天》的评论后做情感聚合发现正向评论集中在“青春”、“回忆”这些词上负向评论集中在“错过”、“也许”这些词汇上这种洞察正是数据分析的意义所在。评论区等你分享你的分析结果也欢迎交流踩坑经历。本文还有配套的精品资源点击获取