Python微博热搜词云分析:从数据清洗到加权可视化
2026/9/15 16:49:21 网站建设 项目流程

简介:本资源是一套面向Python数据采集与可视化初学者的微博热搜话题分析实践项目,聚焦社交媒体舆情洞察场景,解决从数据获取到词云生成的完整技术闭环问题。压缩包共7个Python脚本文件,总大小仅11KB,涵盖热搜爬取(requests+反爬处理)、关键词清洗(jieba分词+停用词过滤)、多时段词云图绘制(wordcloud+matplotlib)及热搜指数统计等核心功能模块,代码结构清晰、注释充分,适合作为NLP入门与Web数据抓取的轻量级练手案例。目前已有2205人学习下载,读者可直接复用爬虫逻辑对接微博热搜接口,快速生成动态词云图,掌握文本预处理、频率统计与可视化呈现的关键步骤,并通过多个‘头条热搜词云图’脚本理解时间维度对比分析的设计思路。

1. 用 Python 快速解析微博热搜榜生成可交互词云,不是做图而是挖语义信号

“近期微博热搜话题榜词云分析.rar”这个标题背后,不是一张静态图片的生成任务,而是一套轻量级舆情信号捕获流程:从原始热搜数据(通常是 HTML 或 JSON 格式)中精准提取话题文本、过滤广告/运营词、归一化实体(如“王宝强”和“宝强”合并)、计算加权频次(考虑热度值、停留时长、上升趋势),最后用可导出、可缩放、带悬停信息的 SVG 或 HTML 词云呈现。它面向的是运营岗快速复盘当日热点、内容编辑预判选题方向、市场人员监测竞品声量——不需要部署服务,不依赖微博官方 API(已限流),单机 Python 脚本 + 本地数据文件即可闭环。关键不在“词云好看”,而在“词频可信”:热搜榜本身含噪声(如“爆”“沸”标签、平台置顶词条、重复话题变体),直接丢进jieba+wordcloud会严重失真。本文就拆解这个.rar文件最可能封装的真实工作流:解压 → 解析结构化数据 → 清洗 → 加权统计 → 可视化,每一步都给出可验证的代码、参数依据和避坑点。

2. 解压与数据结构识别:先确认.rar里藏的是什么格式的热搜快照

微博热搜榜数据源有明确规律:网页端 DOM 结构稳定(<div class="hot-list">包裹<a href="/weibo?q=xxx">标签),移动端 API 返回 JSON(含realtime_list字段),而导出的.rar压缩包通常包含三类典型内容——HTML 页面存档、JSON 格式抓取结果、或 CSV 表格(列名常为rank,topic,hot_num,link)。不能盲目解压后直接读取,必须先探测数据形态,否则后续清洗逻辑全错。

2.1 用rarfile安全解压并检查文件列表

import rarfile import os # 检查 rar 是否可用(避免用户未装 unrar) try: rf = rarfile.RarFile("近期微博热搜话题榜词云分析.rar") file_list = [f.filename for f in rf.filelist] print("压缩包内文件:", file_list) # 输出示例:['weibo_hot_20240520.html', 'README.md'] except rarfile.RarCannotExec: print("系统未安装 unrar,请执行:sudo apt install unrar(Ubuntu)或 brew install unrar(macOS)") exit(1) except Exception as e: print(f"解压失败:{e}") exit(1)

提示rarfile库本身不自带解压引擎,依赖系统unrar命令。若报错RarCannotExec,说明环境缺失底层工具,需按提示安装。Windows 用户可下载UnRAR.exe放入 PATH,或改用py7zr(支持 rar)但需额外处理编码。

2.2 自动识别数据格式:HTML / JSON / CSV 三路分支判断

import json import pandas as pd from bs4 import BeautifulSoup def detect_data_format(filepath): """根据文件扩展名和内容特征判断数据类型""" ext = os.path.splitext(filepath)[1].lower() if ext in ['.html', '.htm']: with open(filepath, 'r', encoding='utf-8') as f: content = f.read(1000) # 读前1KB足够判断 if '<!DOCTYPE html' in content or '<div class="hot-list' in content: return 'html' elif ext == '.json': try: with open(filepath, 'r', encoding='utf-8') as f: json.load(f) # 尝试加载,不报错即为有效 JSON return 'json' except (json.JSONDecodeError, UnicodeDecodeError): pass elif ext in ['.csv', '.txt']: try: df = pd.read_csv(filepath, nrows=5, encoding='utf-8') # 检查是否含热搜典型字段 if any(col in df.columns for col in ['topic', '热词', '关键词']) and 'hot_num' in df.columns: return 'csv' except Exception: pass return 'unknown' # 示例:对解压出的第一个数据文件做探测 sample_file = rf.filelist[0].filename rf.extract(sample_file, path="./temp/") detected_type = detect_data_format(f"./temp/{sample_file}") print(f"检测到数据格式:{detected_type}") # 输出:html / json / csv / unknown
2.2.1 HTML 格式解析:定位<a>标签中的真实话题文本

微博热搜 HTML 中,话题链接<a>href参数q=后是 URL 编码的话题名,text内容可能含“新”“爆”等角标,需剥离:

with open(f"./temp/{sample_file}", 'r', encoding='utf-8') as f: soup = BeautifulSoup(f, 'html.parser') topics = [] for a_tag in soup.select('a[href*="/weibo?q="]'): href = a_tag.get('href', '') # 提取 q= 后的值并解码 import urllib.parse topic = urllib.parse.unquote(href.split('q=')[1].split('&')[0]) # 清洗:移除【】括号内运营标注、数字序号、emoji import re clean_topic = re.sub(r'[【】\d+\s\U0001F300-\U0001F64F\U0001F680-\U0001F6FF]+', '', topic).strip() if clean_topic: # 非空才加入 topics.append(clean_topic) print("提取的原始话题(前5):", topics[:5]) # 输出示例:['华为Pura70发布会', '高考作文题', '端午节放假安排', '特斯拉股价大跌', '周杰伦演唱会门票']
2.2.2 JSON 格式解析:直取realtime_list中的wordnum

微博官方 API 返回 JSON 中,热搜条目通常在data.realtime_list数组,每个元素含word(话题词)、num(热度值)、icon_desc(标签):

with open(f"./temp/{sample_file}", 'r', encoding='utf-8') as f: data = json.load(f) # 兼容不同 JSON 结构:尝试多级路径 topics = [] for item in data.get('data', {}).get('realtime_list', []) or \ data.get('realtime_list', []) or \ data.get('data', []): word = item.get('word') or item.get('topic') or item.get('keyword') num = item.get('num') or item.get('hot_num') or item.get('score', 0) if word and isinstance(num, (int, float)): topics.append({'topic': word, 'hot_num': int(num)}) # 构建 DataFrame 便于后续加权 df = pd.DataFrame(topics) print("JSON 解析结果(热度降序):\n", df.sort_values('hot_num', ascending=False).head(3))
topichot_num
端午节旅游攻略987654
高考数学难度876543
华为鸿蒙NEXT765432
2.2.3 CSV 格式解析:强制指定列名并校验数据完整性

若检测为 CSV,但列名混乱(如标题,热度,链接),需映射为标准字段:

df = pd.read_csv(f"./temp/{sample_file}", encoding='utf-8') # 定义列名映射表(覆盖常见中文/英文别名) col_mapping = { 'topic': ['标题', '话题', '热词', '关键词', 'word'], 'hot_num': ['热度', 'hot_num', 'num', 'score', '搜索指数'], 'link': ['链接', 'url', 'href'] } standard_df = pd.DataFrame() for std_col, possible_names in col_mapping.items(): for name in possible_names: if name in df.columns: standard_df[std_col] = df[name] break else: if std_col == 'hot_num': standard_df[std_col] = 1000 # 默认热度,后续需人工校准 else: standard_df[std_col] = '' # 过滤空话题 standard_df = standard_df[standard_df['topic'].str.strip() != ''].copy() print("CSV 标准化后行数:", len(standard_df))

3. 热搜话题清洗与加权:为什么“苹果”不能和“苹果手机”一起计数

直接统计词频会淹没关键信号:热搜中“苹果”可能指水果、公司、手机品牌;“华为”和“华为Mate60”应归并;“爆”“沸”标签需剔除;平台置顶词条(如“微博之夜”)应降权。清洗不是删词,而是构建语义层级——用规则+词典+简单 NLP 实现低成本归一。

3.1 基础清洗:移除干扰符号、统一空格、过滤无效词

import re def basic_clean(topic): """基础清洗:去噪、标准化""" # 移除【新】【爆】【沸】等角标 topic = re.sub(r'【[^】]+】', '', topic) # 移除末尾数字序号(如“罗永浩 1”→“罗永浩”) topic = re.sub(r'\s+\d+$', '', topic) # 合并连续空格,去首尾空格 topic = re.sub(r'\s+', ' ', topic).strip() # 过滤纯数字、单字、过短词(<2字符) if len(topic) < 2 or topic.isdigit(): return None return topic # 应用清洗 cleaned_topics = [basic_clean(t) for t in topics if basic_clean(t)] print("清洗后话题数:", len(cleaned_topics)) print("清洗示例:", cleaned_topics[:3]) # ['华为Pura70发布会', '高考作文题', '端午节放假安排']

3.2 实体归并:用前缀匹配合并长尾词(无需训练模型)

热搜中高频出现“华为”“华为手机”“华为Mate70”——它们应归为同一实体。采用前缀树(Trie)思想,按长度倒序排序后匹配:

def merge_by_prefix(topics, min_ratio=0.6): """ 归并前缀词:若A是B的前缀,且len(A)/len(B) > min_ratio,则B归并到A 例如:'华为' 是 '华为Mate70' 的前缀,且 2/6 > 0.6 → 归并 """ # 按长度降序排列,确保长词先被处理 sorted_topics = sorted(set(topics), key=len, reverse=True) merged = {} for long_topic in sorted_topics: found_base = False for base in merged.keys(): if long_topic.startswith(base) and len(base)/len(long_topic) >= min_ratio: merged[base] += 1 found_base = True break if not found_base: merged[long_topic] = 1 return merged # 示例:对清洗后的 topics 归并 merged_dict = merge_by_prefix(cleaned_topics) print("归并后实体(频次 top5):", sorted(merged_dict.items(), key=lambda x: x[1], reverse=True)[:5]) # 输出:[('华为', 3), ('高考', 2), ('端午节', 2), ('特斯拉', 1), ('周杰伦', 1)]

3.3 热度加权:融合排名、热度值、停留时长三维度

仅用hot_num会忽略位置效应——第1名和第50名热度值可能接近,但传播力差异巨大。引入加权公式:
最终权重 = hot_num × rank_decay × duration_factor
其中rank_decay = 1 / log2(rank + 1)(模拟注意力衰减),duration_factor来自历史数据(若提供“连续上榜天数”字段则乘以该值,否则默认为1):

import numpy as np # 假设 df 已含 topic, hot_num 列,且按排名顺序(index=0 是第1名) df['rank'] = df.index + 1 df['rank_decay'] = 1 / np.log2(df['rank'] + 1) df['duration_factor'] = df.get('days_on_list', 1) # 若无此列则用1 df['final_weight'] = df['hot_num'] * df['rank_decay'] * df['duration_factor'] # 归一化到 0-100 区间便于词云渲染 df['weight_norm'] = ((df['final_weight'] - df['final_weight'].min()) / (df['final_weight'].max() - df['final_weight'].min() + 1e-8)) * 100 print("加权后数据(含归一化权重):\n", df[['topic', 'hot_num', 'rank', 'final_weight', 'weight_norm']].head())
topichot_numrankfinal_weightweight_norm
华为Pura70发布会9876541987654.0100.0
高考作文题8765432619812.562.8
端午节放假安排7654323482101.248.8

4. 生成高信息密度词云:支持悬停查看热度、导出 SVG、规避字体缺失

wordcloud库默认输出 PNG,无法交互、缩放失真、中文显示常乱码。改用pyecharts生成 HTML 词云,或d3py输出 SVG——本文选择pyecharts,因其零配置支持中文、内置热度悬停、一键导出 SVG/PNG/HTML:

4.1 安装与基础配置:解决中文字体和渲染后端问题

pip install pyecharts==2.0.4 # 固定版本避免新版 breaking change # 下载思源黑体(免费可商用)并指定路径 wget https://github.com/adobe-fonts/source-han-sans/raw/release/SubsetOTF/SOURCEHANSANS-REGULAR.TTF
from pyecharts import options as opts from pyecharts.charts import WordCloud from pyecharts.render import make_snapshot from snapshot_selenium import snapshot # 设置全局字体(关键!否则中文显示方块) import os os.environ["PYECHARTS_FONT_PATH"] = "./SOURCEHANSANS-REGULAR.TTF" # 构造词云数据:[(topic, weight), ...] word_weights = [(row['topic'], round(row['weight_norm'], 1)) for _, row in df.iterrows()] # 创建词云图 wc = ( WordCloud() .add("", word_weights, word_size_range=[12, 60], # 字体大小范围,避免小词不可读 shape="circle", # 可选 'circle','cardioid','diamond' width="100%", height="600px") .set_global_opts( title_opts=opts.TitleOpts(title="微博热搜话题词云(加权热度)"), tooltip_opts=opts.TooltipOpts(is_show=True, formatter="{b}: {c}分"), # 悬停显示 legend_opts=opts.LegendOpts(is_show=False) ) )

4.2 导出为 SVG:保留矢量清晰度,支持浏览器缩放

# 渲染为 HTML(可直接打开) wc.render("weibo_hot_wordcloud.html") # 导出为 SVG(需安装 selenium + chromedriver) try: from pyecharts.render import make_snapshot from snapshot_selenium import snapshot make_snapshot(snapshot, wc.render(), "weibo_hot_wordcloud.svg") print("✅ SVG 导出成功:weibo_hot_wordcloud.svg(双击用浏览器打开,无限缩放)") except ImportError: print("⚠️ selenium 未安装,跳过 SVG 导出。可手动打开 HTML 文件截图。") print(" 安装命令:pip install selenium && 下载 chromedriver 匹配 Chrome 版本")

4.3 关键参数调优表:解决词云常见失真问题

参数作用推荐值失效场景
word_size_range=[12, 60]控制最小/最大字体[10, 70]小词看不清,大词挤出画布
shape="circle"词云形状"cardioid"(心形更紧凑)"star"会导致边缘词截断
mask_image=None蒙版图像传入 numpy array 二值图PNG 蒙版需 alpha 通道,否则白底失效
rotation_step=45旋转角度步长30(减少重叠)设为0强制水平,牺牲多样性
font_path="./SOURCEHANSANS-REGULAR.TTF"中文字体路径绝对路径,勿用相对路径字体文件不存在时显示方块

注意mask_image若启用,需确保蒙版为灰度图且背景为黑色(值为0)、前景为白色(值为255),否则词云会填充到错误区域。调试时先设mask_image=None确认基础效果。

5. 验证词云可信度:用三个命令快速交叉检验数据质量

生成词云只是终点,验证才是专业分水岭。不靠肉眼,用三条 Shell/Python 命令完成数据可信度快检——覆盖完整性、一致性、合理性。

5.1 检查原始数据完整性:确认无空话题、无重复行

# 统计原始 CSV/JSON 中空话题行数(Linux/macOS) awk -F',' '{if($1 ~ /^ *$/ || $1 ~ /^" *"$/) print NR}' weibo_hot.csv | wc -l # 输出 0 表示无空话题 # 检查重复话题(忽略大小写和空格) awk -F',' '{gsub(/^[ \t]+|[ \t]+$/, "", $1); print tolower($1)}' weibo_hot.csv | sort | uniq -d | wc -l # 输出 0 表示无重复

5.2 验证加权逻辑一致性:用 Pandas 一行命令核对权重分布

# 检查 weight_norm 是否严格在 [0,100] 区间 assert df['weight_norm'].min() >= 0 and df['weight_norm'].max() <= 100, "权重越界!检查归一化公式" # 检查 top3 权重是否递减(符合 rank_decay 逻辑) top3_weights = df.nsmallest(3, 'rank')['weight_norm'].tolist() assert top3_weights[0] > top3_weights[1] > top3_weights[2], "排名靠前但权重未更高,rank_decay 失效" print("✅ 权重逻辑验证通过:", top3_weights)

5.3 人工抽样比对:用head -n 5直接查看原始 vs 清洗后效果

# 原始话题(假设存于 raw_topics.txt,每行一个) head -n 5 raw_topics.txt # 输出: # 【爆】华为Pura70发布会 # 高考作文题【新】 # 端午节放假安排 # 特斯拉股价大跌【沸】 # 周杰伦演唱会门票 # 清洗后话题(cleaned_topics.txt) head -n 5 cleaned_topics.txt # 输出: # 华为Pura70发布会 # 高考作文题 # 端午节放假安排 # 特斯拉股价大跌 # 周杰伦演唱会门票

提示:抽样比对必须人工执行——自动化无法替代对语义合理性的判断。重点看三点:① 角标是否清除干净;② “华为Pura70发布会”未被错误切分为“华为 Pura70 发布会”(说明未用 jieba 分词,正确);③ “周杰伦演唱会门票”未被归并为“周杰伦”(说明前缀归并阈值min_ratio=0.6合理,避免过度合并)。

词云不是终点,而是起点。当weibo_hot_wordcloud.svg在浏览器中放大至 400% 仍清晰显示“华为Pura70发布会”时,你已越过数据搬运工的门槛——下一步,把weight_norm列接入告警脚本,当“某竞品词”权重单日增幅超 200% 时自动钉钉通知,这才是词云该有的生产力。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询