从爬虫到词云:基于Python的微博数据采集与可视化实战

发布时间:2026/9/17 2:03:51

从爬虫到词云:基于Python的微博数据采集与可视化实战 简介基于Python爬取新浪微博并生成词云的完整项目主要面向毕业设计、期末大作业和课程设计场景适合有一定Python基础、希望快速搭建爬虫与可视化任务的学生。压缩包共27个文件包含12个Python脚本、5个XML配置、4张效果图片、字体文件、Markdown文档及CSV数据等整体约9.03MB其中Python脚本覆盖Scrapy爬虫逻辑、WordCloud词云生成与中间件配置XML和CFG等负责工程配置PNG与CSV便于查看运行结果与中间数据目录结构清晰。项目代码注释详细即使新手也能读懂下载后简单配置即可部署运行内置文档说明能帮助理解微博数据抓取、清洗与词云生成全流程同时提供完整可运行的爬虫框架便于根据自身需要调整目标关键字和解析规则。作为被导师认可的高分项目质量可靠可直接用于完成期末大作业、毕业设计或课程设计也有助于学习Python爬虫与数据可视化。目前已有310人学习下载。1. 基于Python的新浪微博爬虫与词云生成解决的不只是“怎么爬”的问题一个做竞品舆情的朋友问我怎么把指定博主近一年的微博快速变成一张词云图用来判断对方团队最近在推什么概念。这个问题拆开看并不难先爬数据再分词最后画图。但真正动手时会发现微博的移动端接口返回的JSON字段远比网页版干净而词云输出的乱码和噪声词才是让人头疼的部分。这篇博客就把我自己常用的那套高质量代码路径写清楚覆盖登录态维护、接口解析、数据清洗、中文分词到词云渲染的完整链路。适合有一定Python基础、想直接跑通“采集到可视化”的工程师也适合需要做舆情观察和内容分析的运营技术人员。整个方案基于requests、jieba和wordcloud不需要Scrapy这类重型框架就能在单机环境下稳定运行。2. 新浪微博爬虫的难点与数据链路设计从Cookie到词云输入2.1 微博反爬的几个核心关卡登录态、频率限制、验证码新浪微博的网页版和移动端Web版有各自独立的接口规则。早期很多人抓m.weibo.cn的/api/container/getIndex接口因为它的JSON结构稳定、字段命名清晰而且大部分公开博主的微博不需要额外授权。但接口本身对未登录状态有限流连续请求几十次后会返回msg: 请求过于频繁或者直接返回HTML页面而不是JSON。真正需要突破的是登录态。常见做法有两种一种是用requests.session模拟账号密码登录走login.weibo.cn的加密流程需要处理su参数、RSA加密、gc等字段代码量不小另一种是直接手动从浏览器复制Cookie注入到Session中简单直接适合个人爬取。我一般用后者因为新浪会不定期调整加密参数维护模拟登录的成本远高于复制Cookie的成本。Cookie失效时间通常在几天到两周之间失效后程序会连续报401或返回login相关提示检测到这种信号时就该更换Cookie。频率限制是第二个关键点。移动端接口虽然没有显式的RateLimit头但实际观察下来单IP每秒超过2个请求就会触发风控导致返回的data字段为空。对策是主动限速在每次请求之间sleep 1到2秒并且把请求头里的User-Agent固定成手机浏览器的UA。验证码一般只会在高频访问或异常UA时出现一旦遇到包含passport跳转的响应就该立即停止当前循环等待一段时间再继续。2.2 数据链路设计爬什么、存什么、给词云什么词云生成并不需要微博的全部字段但爬虫阶段最好把原始数据完整存下来因为后续可能要做时间趋势、账号分类或情感分析。我习惯把每条微博保存为一条记录字段包括微博ID、正文、发布时间、转发数、评论数、点赞数、来源设备、是否原创。正文是词云的核心输入但必须经过清洗因为微博正文里含有话题标签#xxx#、Emoji、URL、用户名、以及“转发微博”这类冗余词。数据链路可以设计为爬虫采集 - JSON解析 - 字段提取 - SQLite/CSV存储 - 文本清洗 - 分词 - 词云。其中“文本清洗”不是简单去掉特殊字符而是要用正则把话题标签的内容单独提出来因为话题本身就是关键词比如#AIGC#在词云里应该保留AIGC这个词同时去掉#号。URL和用户一般直接删除因为词云关注的是内容主题而不是互动对象。存储层用SQLite最省事不需要额外安装数据库服务。表结构设计成weibo(id, uid, text, created_at, reposts_count, comments_count, attitudes_count, source)其中id设置为主键text保存清洗前的原始文本清洗操作放到查询到内存后再做。这样如果后续发现清洗规则有误还可以随时从库中重置。2.3 字段与数据清洗表格示例下面是我在实际项目中常用的字段映射表直接对应m.weibo.cn接口返回的JSON结构。接口的cards数组中card_type为9的元素表示普通微博卡片mblog字段里才是正文和统计数据。目标字段JSON路径mblog内清洗规则微博IDid直接用字符串正文text先去掉HTML标签再替换a ....../a链接发布时间created_at转换为YYYY-MM-DD HH:MM:SS转发数reposts_count直接取整评论数comments_count直接取整点赞数attitudes_count直接取整来源设备source去掉a标签保留设备名需要特别注意的是text字段。接口返回的正文是HTML片段例如今天发布了a href/n/AI%E5%88%9B%E4%BD%9CAI创作/a的新版本大家看看效果。#AIGC# a href/p/xxx网页链接/a如果不做处理直接拿去分词会把a href...这类标签整个当成一个词导致词云上出现一长串乱码。所以要先用BeautifulSoup或正则把标签剥掉再单独提取话题。import re def clean_weibo_text(html_text: str) - str: # 去除所有HTML标签 text re.sub(r[^], , html_text) # 将 nbsp; 等实体替换为空格 text text.replace(nbsp;, ).replace(amp;, ) # 提取话题标签内容去掉#号 text re.sub(r#([^#])#, r\1, text) # 去掉 用户名 和 网页链接 text re.sub(r[\w\u4e00-\u9fa5\-], , text) text re.sub(rhttps?://\S, , text) return text.strip()这段代码的顺序不能乱。先剥标签再处理HTML实体然后提话题最后去和链接。如果先去除话题正则可能会把标签里的#误伤。参数上r[\w\u4e00-\u9fa5\-]里的\u4e00-\u9fa5是中文Unicode范围用来匹配中文用户名https?://\S匹配以http或https开头的链接\S会吃掉链接后面的空格所以要在处理链接之前先把话题提取完。3. 用Requests-Session维护登录态抓取指定用户微博并解析3.1 获取Cookie与构建Session手动获取Cookie的路径是在浏览器无痕模式下打开https://m.weibo.cn并登录打开开发者工具复制任意一次请求头里的Cookie值。不要复制整个请求头只要Cookie:后面的字符串。然后在Python中构建一个Session对象把Cookie塞进去。import requests from fake_useragent import UserAgent def build_session(cookie_str: str) - requests.Session: session requests.Session() headers { User-Agent: UserAgent().random, Referer: https://m.weibo.cn/, Origin: https://m.weibo.cn, X-Requested-With: XMLHttpRequest, Cookie: cookie_str, } session.headers.update(headers) return session这里有几个参数很关键。UserAgent用随机移动端UA因为微博移动端接口对桌面UA的检查更严格Referer必须指向m.weibo.cn否则接口会返回403X-Requested-With标识这是一个Ajax请求服务端会返回JSON而不是重定向页面。Cookie因为包含SUB和SUBP这两个核心认证字段所以一定要保证字符串完整不能有换行。构建好Session后先访问一次https://m.weibo.cn/api/config做连通性测试。如果返回{code: 0}说明登录态有效如果返回{code: -100}或包含重定向信息说明Cookie已经过期需要重新复制。3.2 构造Ajax请求获取微博列表移动端按用户维度抓微博的核心接口是https://m.weibo.cn/api/container/getIndex主要参数有typeuid、value用户UID、containerid形如107603加UID、since_id分页游标。第一次请求不传since_id后续每次把返回的since_id传回直到返回空或到达指定页数。def fetch_weibo_list(session, uid: str, since_id: str , page_limit: int 10): containerid f107603{uid} url https://m.weibo.cn/api/container/getIndex results [] cur_since_id since_id for _ in range(page_limit): params { type: uid, value: uid, containerid: containerid, } if cur_since_id: params[since_id] cur_since_id resp session.get(url, paramsparams, timeout10) data resp.json() if data.get(ok) ! 1: break cards data.get(data, {}).get(cards, []) for card in cards: if card.get(card_type) ! 9: continue mblog card.get(mblog, {}) if not mblog: continue results.append(mblog) # 保存since_id用于下一页 cur_since_id data[data][cardlistInfo][since_id] # 控制频率避免触发风控 time.sleep(1.5) return results, cur_since_id这段代码中card_type ! 9的判断很重要因为接口返回的cards里混着广告卡片、关注推荐卡片和生日提醒卡片只有9才是普通微博。cardlistInfo.since_id是分页游标它不是数字页码而是服务端生成的不透明字符串所以必须原样传回。time.sleep(1.5)是请求间隔实测低于1秒会频繁触发空数据返回。如果接口返回的data字段是None但ok却是1多半是因为该用户设置了隐私保护或者微博被删除。这种情况直接跳过即可不需要重试。3.3 解析JSON并入库SQLite存储拿到mblog列表后解析并写入SQLite。为了避免重复查询和插入时主键冲突使用INSERT OR REPLACE。import sqlite3 import time def parse_and_store(mblog_list, uid, db_pathweibo.db): conn sqlite3.connect(db_path) conn.execute(CREATE TABLE IF NOT EXISTS weibo ( id TEXT PRIMARY KEY, uid TEXT, text TEXT, created_at TEXT, reposts_count INTEGER, comments_count INTEGER, attitudes_count INTEGER, source TEXT )) for m in mblog_list: row ( str(m[id]), uid, m.get(text, ), m.get(created_at, ), int(m.get(reposts_count, 0)), int(m.get(comments_count, 0)), int(m.get(attitudes_count, 0)), re.sub(r[^], , m.get(source, )) if m.get(source) else , ) conn.execute(INSERT OR REPLACE INTO weibo (id, uid, text, created_at, reposts_count, comments_count, attitudes_count, source) VALUES (?,?,?,?,?,?,?,?), row) conn.commit() conn.close()这里需要重点说明created_at字段。接口返回的值类似字符串因为微博客户端会有“刚刚”“5分钟前”这种相对时间但在请求参数中指定times字段或在Cookie带上相关参数时服务端会返回标准格式。稳妥的做法是存原始值后续需要时间分析时再在Python里转换。另外source字段同样包含HTML标签比如a href... relnofollowiPhone客户端/a解析时不能直接用要用正则剥掉a标签。3.4 频率控制与异常重试参数表微博爬虫的高质量不在于代码多么华丽而在于异常处理是否完整。下面的表是我在项目里沉淀下来的重试参数按接口响应特征来判断处理方式。响应特征可能原因处理方式重试参数返回args为{}ok1该页无数据结束翻页不重试返回login相关或跳转passportCookie失效停止通知人工更换Cookie不重试status_code418IP被风控sleep 120秒后重试最多3次连续3次超时网络或服务端波动指数退避重试初始2秒乘2返回code-100参数缺失或uid错打印参数检查uid不重试重试时不能无限循环否则IP会被拉进黑名单。我一般用一个简单的循环计数器超过最大重试次数就写日志并跳出。日志里记录当前uid和since_id这样断点续爬时可以直接从上次的游标开始。4. 中文分词与词云生成jieba wordcloud的关键参数4.1 从微博文本到干净分词停用词与过滤爬下来的微博正文直接丢给jieba分词会得到大量“了”“的”“在”“是”这类无意义词汇而且“转发微博”“网页链接”这类高频噪声词也会占据高权重。所以必须构建停用词表并在分词前先做一次定向清洗。常见的做法是准备一个stopwords.txt文件每行一个词。内置词表可以先用哈工大停用词表、百度停用词表再结合微博场景手动添加“转发微博”“分享图片”“网页链接”“评论配图”等词组。加载后用列表推导过滤即可。import jieba def load_stopwords(pathstopwords.txt): with open(path, r, encodingutf-8) as f: return set(line.strip() for line in f) def tokenize_for_wordcloud(texts, stopwords): word_list [] for text in texts: # text 应该是清洗后的纯文本见2.3 seg_list jieba.cut(text, cut_allFalse) for word in seg_list: word word.strip() if len(word) 2: continue if word.isdigit(): continue if word not in stopwords: word_list.append(word) return word_listcut_allFalse表示精确模式适合提取组合词如果设为True会把“人工智能”切分成“人工”“智能”等多个词词云上出现大量单字和双字碎片不推荐。len(word) 2用来过滤单字和空字符串但要注意有些品牌名是单字如“燃”所以过滤条件可以根据实际语料灵活调整。word.isdigit()过滤纯数字避免“666”“2025”这类词干扰主题。4.2 wordcloud字体、形状与调参中文词云最典型的坑是乱码方块因为wordcloud默认字体不支持中文。解决方法是显式指定中文字体路径Linux下常见路径是/usr/share/fonts/truetype/wqy/wqy-zenhei.ttcWindows下可以用C:/Windows/Fonts/simhei.ttf。如果找不到系统字体可以从项目目录里放一个字体文件用相对路径引用。形状上默认是矩形也可以传入mask参数指定一张黑白图片作为轮廓。注意mask图片中白色部分会被忽略黑色部分才是词云绘制区域。下面是一个完整的词云生成函数import wordcloud def generate_wordcloud(word_list, output_pathwordcloud.png): # 统计词频 freq {} for w in word_list: freq[w] freq.get(w, 0) 1 wc wordcloud.WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, width1600, height900, background_colorwhite, max_words200, colormapviridis, margin2, random_state42, ) wc.generate_from_frequencies(freq) wc.to_file(output_path) return wc这里的关键参数有几个。max_words控制词云上显示的词条数量设成200比较合适太少了信息量不够太多了画面拥挤。colormap用viridis这种渐变色比默认的橙色好看但如果是舆情报告需要黑白打印建议用gray。random_state固定随机种子保证重复生成时词的位置布局一致方便做前后对比。margin控制词与词之间的间隙太大会显得稀疏太小会重叠。4.3 词频统计与过滤的细节generate_from_frequencies接收字典时字典的键必须是字符串值必须是数字。如果直接传入Counter对象也可以但我习惯手动构建字典因为可以在这一步把低频词过滤掉。比如设定freq中词频小于2的词直接丢弃减少背景噪声。另外要注意词频的分布问题。如果某一个词出现频率特别高比如博主反复提及自己的产品名词云会让这个词的字体变得巨大挤压其他词的空间。这时可以在生成词云前对词频做一次对数压缩把极值拉平。但不是所有场景都需要如果希望突出核心词就不压缩。import math def normalize_freq(freq, log_scaleFalse): if not log_scale: return freq return {k: int(math.log(v) * 10) 1 for k, v in freq.items()}使用math.log时要注意词频为1时对数是0所以后面要加1保证权重为正。这个技巧在词云图里效果明显能让头部词和长尾词在尺寸上的差异变得温和。5. 词云质量验证与增量爬取技巧5.1 多用户合并与时间范围过滤单个博主的词云只能反映一个人的表达习惯做竞品分析时需要把多个账号的微博合并后再生成词云。合并方式很简单把多个uid的抓取结果都写进同一个SQLite表生成前用WHERE uid IN (...)查询即可。但这里要注意如果某两个账号频繁互动对方的内容会产生重复词建议在清洗阶段去掉所有用户名。时间范围过滤也常用因为长期词云会掩盖近期变化。SQL可以用strftime函数处理created_at如果字段存的是标准格式直接加WHERE created_at 2024-01-01 AND created_at 2025-01-01。微博接口返回的created_at如果有“刚刚”这种相对时间需要先转换再存库否则时间过滤会失效。5.2 词云合理性验证词频排序与人工抽检生成词云图后不要只看图要检查原始词频排序。我的做法是把分词结果做一个Counter打印前30个高频词核对有没有明显无意义的词漏网。from collections import Counter def check_top_words(word_list, top_n30): counter Counter(word_list) for word, count in counter.most_common(top_n): print(f{word}\t{count})如果发现前几名里有“转发”“图片”这类词说明停用词表没覆盖完整。此时回到stopwords.txt添加这些词重新生成词云。另外还要注意中英文混排问题wordcloud本身无法区分中英文的宽度差异如果文本里有大量英文缩写建议统一转大写或小写否则同一个词可能以AI和ai两种形式出现。5.3 增量爬取用since_id避免重复完整爬取一次之后后续只需要抓取新增微博。观察接口返回的since_id机制可以发现第一次请求不带since_id返回的是最新微博后续每次带since_id翻到更早的内容。所以增量爬取时只需要记录上次最新一条微博的ID然后从最新页迭代到某一条ID为止。def incremental_fetch(session, uid, stop_id, max_pages5): results [] since_id for _ in range(max_pages): page_data, since_id fetch_weibo_list(session, uid, since_id, page_limit1) if not page_data: break for m in page_data: if str(m[id]) str(stop_id): return results results.append(m) return results这里stop_id是上一次爬取时库里最新的微博ID当接口返回的微博ID等于它时就说明后面的内容都是旧的直接停止。max_pages设成5是为了安全限制防止因为ID异常导致脚本无限翻页。增量爬取配合cron定时任务可以每天自动更新词云数据。最后再提一个微妙但实用的技巧词云的背景色不要用深色除非你有意做海报。深色背景在打印和PPT展示时会对字体颜色产生限制而且wordcloud默认字体透明度调节在深色背景上容易糊。用白色背景配深色文本是舆情报告里最稳妥的选择。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/17 2:03:51

RS-485接口芯片选型避坑指南:从MAX485CPA+看可靠通信设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 2:03:51

Torch-TensorRT 源码评测:5393 文件、编译链路与版本对齐

"5393"这个数字,是我把 Torch-TensorRT 的仓库完整 clone 下来、跑完一次文件级清点之后,屏幕上第一眼跳出来的东西。当时我正准备把一个大模型推理服务从纯 PyTorch 迁到 TensorRT,想找个中间层省点事,Torch-TensorRT …

2026/9/17 2:03:51

微信小程序自定义顶部导航栏:从原理到全机型适配实践

简介:微信小程序原生顶部导航栏在样式定制和不同机型适配上有不少限制,这份完整实例正面向有自定义导航栏需求的小程序开发者,演示如何先在 app.json 中关闭原生导航栏,再通过自定义 navigationBar 组件构建统一头部,并…

2026/9/17 2:53:56

企业级AI Agent平台建设方案:从架构分层到工程治理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 2:53:56

HC/LC/AC/FT四种经典显著性检测算法C++实现

简介:本资源是一套面向计算机视觉初学者与研究者的显著性检测算法实践代码包,聚焦HC、LC、AC、FT四类经典方法,解决图像中关键区域自动定位问题,适用于图像摘要、目标检测预处理、交互式编辑等实际场景。压缩包共16个文件&#xf…

2026/9/17 2:53:56

中国九大农业区SHP数据处理:坐标系统、投影转换与实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 2:53:56

从TOPS到Token/sec:AI芯片算力指标深度解析与实测指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 2:48:56

六轮DES差分分析:用C++实现S盒差分表与子密钥投票恢复

简介:基于C的六轮DES算法差分分析实现,面向密码学课程设计与差分密码分析入门者,完整呈现选择明文攻击的工程化落地流程。代码按模块划分为四部分:6轮DES加解密模块用于产生明密文对与最终验证;差分分析表生成模块用于…

2026/9/16 12:52:37

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/17 0:03:13

WiFi密码安全测试:从原理到实战的字典暴力破解指南

1. 写在前面:我为什么要研究WiFi密码这件事先交代一下背景。我身边有不少朋友,家里的WiFi密码常年是"12345678"或者"88888888",问就是"好记"。直到有一次,隔壁邻居蹭网蹭到我家路由器后台都进不去&…

2026/9/17 0:03:13

redis-py服务控制与监控函数实战:从ping到slowlog的巡检指南

我用 redis-py 写了快五年的业务代码,坦白说,真正让我觉得这个客户端“像一个成熟工具箱”的,不是 get/set 那套基本操作,而是它那批专门做服务控制与状态监控的辅助函数。日常开发里,大家把redis.Redis(host..., deco…

2026/9/17 0:03:13

SpringBoot+Vue3实现中小企业设备管理系统开发实践

1. 项目概述与核心价值中小企业设备管理系统是制造业、服务业等领域的基础信息化工具。传统设备管理往往依赖Excel表格或纸质记录,存在数据孤岛、流程混乱、维护成本高等痛点。这套基于Java SpringBootVue3MyBatis的技术方案,通过前后端分离架构实现了设…

2026/9/16 22:55:57

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/16 22:56:09

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/16 22:56:16

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码