Python调用百度云API实现微博评论情感分析:从数据清洗到可视化
简介这份资源面向希望入门文本情感分析与API调用的Python学习者围绕微博评论情感偏向判断这一典型场景展开。包内提供可供参考的微博评论数据集以及调用百度云API获取文字情感得分、再对得分进行标准化处理以得到实际倾向的脚本帮助读者理解从原始评论到情感倾向的完整链路。资源共15个文件以py脚本、xml配置、xls表格与csv数据为主另有少量工程配置文件压缩包约421KB结构轻量便于快速上手。已有202人学习下载。通过这份资料读者可以掌握百度情感分析接口的调用方式、情感得分的标准化思路以及pandas等库处理评论数据的基本方法并可将该流程迁移到品牌口碑监测、公共事件舆情分析等场景适合作为课程作业或小型项目的参考实现。1. 微博评论情感分析从百度云 API 到 Python 落地这套源码能跑通什么做舆情或者运营分析的同行大概率都遇到过这种需求手里攥着几千条微博评论想知道用户到底是夸还是骂比例大概多少哪几个词被反复提及。人工一条条看眼睛看花了也读不完而且主观判断还不一致。这套基于 Python 调用百度云 API 的微博评论情感偏向分析源码解决的就是这个场景——把评论数据丢进去自动输出每条评论的情感极性积极、消极、中性以及对应的置信度分数再汇总成可视化图表。它适合三类人一是刚学完 Python 基础、想找一个完整项目练手的入门者二是需要快速搭建舆情监控原型的运营或产品岗三是想了解情感分析 API 调用流程、但不想从零训练模型的后端开发。整套流程不依赖本地 GPU核心计算在云端完成本地只需要跑通数据清洗和请求调度。2. 百度云情感分析 API 的接入逻辑与 Python 请求封装2.1 为什么选百度云 API 而不是本地模型情感分析这件事本地跑模型和调云端 API 是两条路。本地模型比如 SnowNLP、BERT 微调版好处是数据不出本地、没有调用次数限制但缺点也很明显SnowNLP 对网络用语和反讽的识别率堪忧BERT 微调需要标注数据加 GPU 训练周期长。百度云的情感分析 API 属于短文本情感倾向分析底层是百度 NLP 的预训练模型对微博这种口语化、带表情符号的短文本做了专门优化。免费额度方面个人认证后每天有一定量的免费调用次数对于几千条评论的分析需求完全够用。选它的核心理由就一个省去模型训练和调参的环节把精力放在数据清洗和结果解读上。常见做法是先去百度智能云控制台创建应用拿到 API Key 和 Secret Key然后用这两个凭证换取 access_token后续每次请求带上 token 即可。token 有有效期一般是 30 天过期需要重新获取。这个流程在源码里通常封装成一个独立的 auth 模块。2.2 获取 access_token 的代码实现import requests import json # 百度云控制台创建应用后获得的凭证 API_KEY 你的API_KEY SECRET_KEY 你的SECRET_KEY def get_access_token(api_key, secret_key): 用 API Key 和 Secret Key 换取 access_token token 有效期 30 天建议缓存到本地文件避免重复请求 url https://aip.baidubce.com/oauth/2.0/token params { grant_type: client_credentials, client_id: api_key, client_secret: secret_key } resp requests.post(url, paramsparams, timeout10) result resp.json() if access_token in result: return result[access_token] else: raise Exception(f获取 token 失败: {result}) # 调用示例 token get_access_token(API_KEY, SECRET_KEY) print(ftoken 前 20 位: {token[:20]}...)这段代码的逻辑很直接向百度云的 OAuth 接口发一个 POST 请求参数里带上 grant_type、client_id 和 client_secret。返回的 JSON 里如果包含 access_token 字段就说明成功否则抛出异常。参数方面timeout 设 10 秒是防止网络波动导致程序卡死实际调试时如果频繁超时可以适当调大到 15 秒。注意 token 不要硬编码在代码里提交到公开仓库源码里一般会用一个 config.py 或者环境变量来存。2.3 情感分析接口的请求封装与批量处理拿到 token 之后就可以调用情感分析接口了。百度云的情感分析接口地址是https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify请求方式是 POSTbody 里放 text 字段。单条请求返回的结果包含 positive_prob积极概率、negative_prob消极概率、confidence置信度和 sentiment0 表示消极1 表示中性2 表示积极。import time def analyze_sentiment(text, access_token): 调用百度云情感分析接口分析单条文本 text: 待分析的微博评论内容 access_token: 上一步获取的 token 返回: 包含情感极性、置信度、积极/消极概率的字典 url https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify params {access_token: access_token} payload {text: text} headers {Content-Type: application/json} try: resp requests.post(url, paramsparams, datajson.dumps(payload), headersheaders, timeout10) result resp.json() if items in result: item result[items][0] return { text: text, sentiment: item.get(sentiment), confidence: item.get(confidence), positive_prob: item.get(positive_prob), negative_prob: item.get(negative_prob) } else: return {text: text, error: result.get(error_msg, 未知错误)} except Exception as e: return {text: text, error: str(e)} # 批量处理时加延时避免触发 QPS 限制 def batch_analyze(texts, access_token, delay0.5): results [] for i, text in enumerate(texts): res analyze_sentiment(text, access_token) results.append(res) if (i 1) % 10 0: print(f已处理 {i1}/{len(texts)} 条) time.sleep(delay) # 控制请求频率 return results这里有几个参数需要留意。delay 设 0.5 秒是保守估计百度云情感分析接口的默认 QPS 限制是 2也就是每秒最多 2 次请求。如果你的账号等级更高可以适当减小 delay。text 字段有长度限制一般不超过 512 个字符微博评论通常不会超但如果遇到长评论需要先截断。返回结果里的 sentiment 字段是整数枚举0 代表消极1 代表中性2 代表积极confidence 是 0 到 1 之间的浮点数越接近 1 表示模型越确定。3. 微博评论数据的清洗与预处理从原始文本到可分析格式3.1 微博评论的典型噪声与清洗策略微博评论的脏数据程度做过的人都有体会。常见的噪声包括表情符号[微笑]、[doge]、提及、话题标签#话题#、URL 链接、重复字符“哈哈哈哈哈哈”、以及各种火星文和拼音缩写。这些东西如果不处理会直接影响情感分析的准确率。比如“[微笑]”这个表情在百度云的模型里可能被识别为积极但在实际语境中它经常是反讽这就是玄学部分了API 也救不了。清洗策略我一般分三步走第一步用正则去掉 URL、提及和话题标签第二步把表情符号替换成对应的文字描述比如 [微笑] 替换成“微笑”[泪] 替换成“流泪”这样模型至少能捕捉到情绪线索第三步处理重复字符把连续三个以上的相同字符压缩成一个比如“哈哈哈哈”变成“哈”。源码里通常会有一个 clean_text 函数把这些逻辑串起来。import re def clean_text(text): 清洗微博评论中的噪声 1. 去除 URL 链接 2. 去除 提及 3. 去除话题标签的 # 号但保留话题内容 4. 表情符号转文字 5. 压缩重复字符 # 去除 URL text re.sub(rhttp[s]?://\S, , text) # 去除 提及 text re.sub(r[\w\u4e00-\u9fa5], , text) # 话题标签去掉 # 保留内容 text re.sub(r#([^#])#, r\1, text) # 常见表情替换 emoji_map { [微笑]: 微笑, [哈哈]: 哈哈, [泪]: 流泪, [怒]: 愤怒, [心]: 喜欢, [doge]: 狗头, [允悲]: 无奈, [费解]: 困惑 } for emoji, word in emoji_map.items(): text text.replace(emoji, word) # 压缩重复字符三个及以上变一个 text re.sub(r(.)\1{2,}, r\1, text) # 去除首尾空白 return text.strip() # 测试 raw 这个产品真的太好用了[微笑] 哈哈哈哈哈哈 小明 #好用# http://t.cn/abc print(clean_text(raw)) # 输出: 这个产品真的太好用了 微笑 哈 小明被去掉 好用这段代码里正则\1{2,}的意思是匹配同一个字符连续出现 3 次及以上然后替换成单个字符。表情映射表可以根据你的数据集特点扩充微博的表情代码有几百个源码里一般只覆盖高频的几十个。清洗完之后建议把空字符串和长度小于 2 的评论过滤掉这些通常是“顶”、“赞”之类的无意义内容分析价值不大。3.2 数据去重与采样避免分析结果被水军带偏微博评论里水军和复制粘贴的内容占比不低如果不做去重分析结果会被少数重复文本主导。去重逻辑很简单用 Python 的 set 或者 pandas 的 drop_duplicates 就行。但要注意完全相同的评论可能是真实用户的不同表达也可能是水军刷的这个边界需要你自己判断。我一般会先做完全去重然后统计重复次数最高的前几条人工看一眼是不是水军话术。import pandas as pd # 假设 df 是包含 comment 列的 DataFrame df pd.DataFrame({comment: [好用, 好用, 不好用, 一般, 好用]}) # 完全去重 df_dedup df.drop_duplicates(subset[comment], keepfirst) print(f去重前: {len(df)} 条, 去重后: {len(df_dedup)} 条) # 统计重复次数 dup_counts df[comment].value_counts() print(重复次数最高的评论:) print(dup_counts.head(3))去重之后如果数据量还是很大比如超过一万条可以考虑分层采样。按评论的点赞数或者时间分布来分层保证样本的代表性。这一步不是必须的但如果你的 API 调用次数有限采样能帮你用更少的请求得到有统计意义的结果。3.3 中文分词的辅助作用为词云和关键词提取做准备情感分析 API 返回的是整句的极性但如果你还想知道哪些词被频繁提及就需要做中文分词。源码里一般会用 jieba 分词然后统计词频最后生成词云。分词之前记得把停用词表加载进来过滤掉“的”、“了”、“是”这些无意义的高频词。import jieba from collections import Counter def extract_keywords(texts, top_n20): 对清洗后的评论做分词和词频统计 texts: 清洗后的评论列表 top_n: 返回词频最高的前 N 个词 # 加载停用词表源码里通常自带一个 stopwords.txt stopwords set() try: with open(stopwords.txt, r, encodingutf-8) as f: for line in f: stopwords.add(line.strip()) except FileNotFoundError: # 兜底内置一个最小停用词集 stopwords {的, 了, 是, 在, 我, 有, 和, 就, 不, 人, 都, 一, 一个} all_words [] for text in texts: words jieba.lcut(text) for w in words: if len(w) 1 and w not in stopwords: all_words.append(w) counter Counter(all_words) return counter.most_common(top_n) # 示例 comments [这个产品真的很好用, 物流太慢了, 质量不错下次还来, 客服态度差] print(extract_keywords(comments, top_n5))分词结果的好坏直接取决于清洗质量。如果评论里还残留着 URL 或者表情代码分词会把它们切成奇怪的碎片。所以顺序不能乱先清洗再去重最后分词。停用词表建议用哈工大停用词表或者百度停用词表源码包里一般会附带。4. 情感分析结果的可视化与统计把数字变成能看懂的图表4.1 情感极性分布饼图与柱状图分析完几千条评论你得到的是一个包含 sentiment 字段的列表。最直观的展示方式就是饼图看积极、中性、消极各占多少比例。用 matplotlib 画饼图注意中文字体要设置好否则会出现方块乱码。Windows 下一般用 SimHeiMac 下用 Arial Unicode MSLinux 下如果没有中文字体需要先安装字体文件。import matplotlib.pyplot as plt import matplotlib # 设置中文字体避免乱码 matplotlib.rcParams[font.sans-serif] [SimHei] # Windows # matplotlib.rcParams[font.sans-serif] [Arial Unicode MS] # Mac matplotlib.rcParams[axes.unicode_minus] False def plot_sentiment_pie(results): 绘制情感极性分布饼图 results: analyze_sentiment 返回的结果列表 # 统计各极性数量 counts {0: 0, 1: 0, 2: 0} for r in results: if sentiment in r and r[sentiment] is not None: counts[r[sentiment]] 1 labels [消极, 中性, 积极] sizes [counts[0], counts[1], counts[2]] colors [#ff6b6b, #feca57, #48dbfb] plt.figure(figsize(8, 6)) plt.pie(sizes, labelslabels, colorscolors, autopct%1.1f%%, startangle140) plt.title(微博评论情感极性分布) plt.axis(equal) plt.savefig(sentiment_pie.png, dpi150, bbox_inchestight) plt.show() # 假设 results 是批量分析后的结果列表 # plot_sentiment_pie(results)饼图适合看整体比例但如果你还想看不同时间段的情感变化柱状图或者折线图更合适。比如按天统计积极评论的数量能看出舆情走势。源码里通常会提供一个按时间聚合的函数把评论的发布时间和情感结果关联起来。4.2 词云生成一眼看出高频关键词词云是舆情分析里最讨喜的可视化形式虽然它不够严谨但汇报的时候很直观。用 wordcloud 库生成词云需要指定中文字体路径否则中文会显示成方块。背景色一般设白色颜色方案用 matplotlib 的 colormap。from wordcloud import WordCloud def generate_wordcloud(texts, output_pathwordcloud.png): 根据评论列表生成词云图 texts: 清洗后的评论列表 output_path: 图片保存路径 # 把所有评论拼成一个长字符串 combined .join(texts) wc WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, # 中文字体路径 width800, height400, background_colorwhite, max_words100, colormapviridis ) wc.generate(combined) wc.to_file(output_path) print(f词云已保存到 {output_path}) # 调用示例 # generate_wordcloud([r[text] for r in results if text in r])font_path 这个参数是必须的不设的话中文全是框框。Linux 服务器上如果没有中文字体可以下载一个 simhei.ttf 放到项目目录然后写相对路径。max_words 控制显示多少个词一般 100 到 200 之间比较合适太多会挤在一起看不清。4.3 置信度过滤低置信度的结果怎么处理百度云 API 返回的 confidence 字段表示模型对判断的确定程度。实际跑下来大部分结果的 confidence 在 0.8 以上但总有一些模棱两可的评论比如“呵呵”、“还行吧”confidence 可能只有 0.5 左右。这些低置信度的结果如果直接算进统计会拉低分析的可靠性。我一般会设一个阈值比如 0.7低于这个值的评论单独拿出来人工复核或者标记为“不确定”不纳入最终的极性比例计算。源码里可以加一个过滤函数def filter_by_confidence(results, threshold0.7): 按置信度过滤结果 返回: (高置信度结果列表, 低置信度结果列表) high_conf [] low_conf [] for r in results: if confidence in r and r[confidence] is not None: if r[confidence] threshold: high_conf.append(r) else: low_conf.append(r) else: low_conf.append(r) # 出错的结果也归入低置信度 return high_conf, low_conf # 使用 # high, low filter_by_confidence(results, threshold0.7) # print(f高置信度: {len(high)} 条, 低置信度: {len(low)} 条)阈值设多少取决于你的容忍度。舆情监控场景下宁可漏报不可误报阈值可以设高一点比如 0.8。如果是学术研究可以设 0.6 并报告置信度分布。这个参数没有标准答案源码里一般会把它做成可配置的。5. 避坑与常见问题排查token 失效、QPS 超限、编码错误5.1 token 过期导致 401 错误现象程序跑了一段时间后突然报错返回{error_code: 110, error_msg: Access token invalid or no longer valid}。原因access_token 的有效期是 30 天过期后需要重新获取。如果你把 token 硬编码在代码里过了一个月肯定失效。解决把 token 获取逻辑封装成自动刷新每次请求前检查 token 是否过期或者干脆每次运行程序都重新获取一次 token。源码里常见做法是用一个 token_cache.json 文件存 token 和获取时间超过 25 天就自动刷新。5.2 QPS 超限返回 18 错误码现象批量分析时前几条正常后面开始返回{error_code: 18, error_msg: Open api qps request limit reached}。原因百度云情感分析接口默认 QPS 是 2你的请求频率超过了这个限制。解决在批量处理的循环里加 time.sleepdelay 至少 0.5 秒。如果数据量大可以考虑用多线程但控制并发数或者申请提高 QPS 配额。注意不要用多进程猛刷容易被封。5.3 中文编码问题导致请求失败现象请求返回{error_code: 282004, error_msg: text param error}或者乱码。原因Python 的 requests 库在发送 JSON 时默认用 UTF-8但如果你的文本里包含特殊字符或者从 CSV 读取时编码不对就会出问题。解决读取 CSV 时指定 encodingutf-8如果报 UnicodeDecodeError 就试 gbk。发送请求时用json.dumps(payload, ensure_asciiFalse)确保中文不被转义。源码里一般会在文件读取和请求发送两处都做编码处理。5.4 空文本和超长文本的处理现象某些评论清洗后变成空字符串或者超过 512 字符导致接口返回错误。原因微博评论里有些只有表情或者 清洗后内容为空有些长评论超过接口限制。解决在调用 API 之前加一个判断空文本直接跳过并标记为“无效”超长文本截断到 500 字符以内。源码里通常会在 clean_text 之后加一个 validate_text 函数做校验。5.5 返回结果中 sentiment 字段缺失现象部分请求返回的 items 里没有 sentiment 字段或者 sentiment 为 None。原因百度云对某些特殊文本比如纯数字、纯英文可能不返回情感极性或者接口内部处理异常。解决在解析结果时用.get(sentiment)而不是[sentiment]避免 KeyError。对于缺失的结果标记为“未知”不纳入统计。如果缺失比例过高检查一下是不是文本太短或者全是特殊符号。6. 进阶技巧用异步请求把分析速度提上来同步请求一条条发几千条评论要等很久。我实测过按 QPS 为 2 算1000 条评论需要 500 秒将近 10 分钟。如果数据量上万这个时间就不可接受了。进阶做法是用 aiohttp 做异步请求在 QPS 限制内把并发跑满。注意异步不是让你突破 QPS 限制而是让你在等待网络响应的时候去发下一个请求把时间利用率提上去。import aiohttp import asyncio import json async def async_analyze(session, text, access_token, semaphore): 异步版情感分析请求 semaphore: 并发控制信号量建议设为 QPS 值 url https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify params {access_token: access_token} payload {text: text} headers {Content-Type: application/json} async with semaphore: try: async with session.post(url, paramsparams, datajson.dumps(payload, ensure_asciiFalse), headersheaders, timeout10) as resp: result await resp.json() if items in result: item result[items][0] return {text: text, sentiment: item.get(sentiment), confidence: item.get(confidence)} return {text: text, error: result.get(error_msg)} except Exception as e: return {text: text, error: str(e)} async def batch_analyze_async(texts, access_token, qps2): 异步批量分析用信号量控制并发数等于 QPS semaphore asyncio.Semaphore(qps) async with aiohttp.ClientSession() as session: tasks [async_analyze(session, text, access_token, semaphore) for text in texts] results await asyncio.gather(*tasks) return results # 调用 # results asyncio.run(batch_analyze_async(texts, token, qps2))这段代码的关键在asyncio.Semaphore(qps)它保证同时最多有 qps 个请求在飞。qps 设 2 就是遵守百度云的限制设太高会被限流。异步版跑 1000 条评论实际耗时大概在 500 秒左右和同步差不多因为瓶颈在 QPS 不在网络延迟。但如果你的账号 QPS 配额更高比如 10异步版就能把速度提升 5 倍。所以这个技巧的价值在于当你申请了更高配额后不用改代码逻辑只改 qps 参数就能提速。另一个技巧是结果缓存。同样的文本没必要重复请求用一个字典把 text 和结果存起来下次遇到相同文本直接读缓存。微博评论里重复内容不少缓存能省下可观的调用次数。我一般会在批量分析函数里加一个 cache 字典命中缓存就跳过请求。从那以后我每次跑批量分析都强制先做去重和缓存检查再发请求。这个习惯帮我省下了至少三成的 API 调用量也避免了因为重复请求触发限流。希望帮到你。本文还有配套的精品资源点击获取