ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Python的网络舆情分析系统实战:从爬虫采集到情感可视化全流程

基于Python的网络舆情分析系统实战:从爬虫采集到情感可视化全流程 简介这是一套面向高校人工智能课程设计与期末大作业的基于Python的网络舆情分析系统完整项目涵盖源码、全部数据与文档说明。项目已调试可运行无需修改即可直接用于答辩或提交适合需要快速获得高质量完成方案的学生。压缩包共118个文件大小45.22MB主要包含Python业务代码py、Java辅助模块java/class、界面与配置文件jar/xml/json、说明文档md/txt以及示例图表等目录结构清晰便于按模块理解舆情采集、清洗、分析与可视化流程。目前已有1476人学习下载。资源的价值在于不仅提供可复用的完整代码还附带了数据文件、文档说明及可视化展示组件如柱状图、饼图并集成了外部NLP分析能力可帮助读者掌握从数据获取到情感研判的完整链路同时为课程答辩提供充分支撑。1. 人工智能大作业“基于Python的网络舆情分析系统”从选题到交付一共要跨几道坎先说结论网络舆情分析这个人工智能大作业卡人的地方从来不是算法有多深而是从 python 爬虫采集、文本清洗、情感分析到可视化这条数据链路能不能闭环。题目里挂着「人工智能」实际评阅人最在意的是你的系统能不能输入一个时间段、输出一张说得清讲得明的舆情结论图至于用没用深度学习反而排在后面。这个项目的典型交付物包括一套可运行源码、一份可复现的爬取与预处理脚本、若干清洗后的数据集以及一份能把每个模块讲清楚的文档说明。适合两类人一是人工智能方向的学生想拿一份完整大作业交差二是刚入门 NLP 的工程师想用舆情分析练手把爬虫、分词、情感模型、图表报告串起来。2. 先立系统骨架数据层、算法层、展示层的模块边界与选型理由2.1 为什么选五段式架构爬虫采集、文本预处理、情感分析、主题聚类、可视化我见过不少舆情分析大作业翻车翻在最常见的一种做法上把所有代码塞进一个 notebook从上到下跑完图是出来了但老师一问“数据从哪来、清洗规则是什么、情感阈值为什么这么定”就答不上来。所以建议一开始就按五段式把系统切开爬虫采集、文本预处理、情感分析、主题聚类、可视化。这五个模块之间的数据流是单向的采集层产出原始 HTML 或 JSON预处理层产出干净的分词文本分析层产出情感分数与主题编号展示层把结果画成词云、趋势线和饼图。模块之间只用 CSV 文件或 DataFrame 衔接不搞对象之间深耦合。这样做的直接好处是任何一个环节出问题你只需要替换掉那一段不用把整个系统推倒重来。对课程作业来说这也是最容易在文档里画架构图的结构。第一版不要贪多。爬虫只做 1 到 2 个新闻或社交平台的数据源情感分析先跑词典法主题聚类直接落 LDA可视化用 pyecharts 或 wordcloud足够覆盖一个大作业的工作量。在这套骨架里你后续想加 BERT 情感模型、加实时舆情大屏都是在固定接口上做替换不用改整体设计。2.2 一组不翻车的技术选型requests、jieba、SnowNLP、pyecharts网络舆情分析系统的技术选型第一条原则是“老师能复现”。凡是需要联网下载大模型权重、需要特殊硬件支持的方案都容易在答辩现场变成黑匣子。我一般推荐这套组合requests BeautifulSoup 搞定采集jieba 做中文分词SnowNLP 或朴素贝叶斯做情感判断gensim 做 LDA 主题模型最后用 pyecharts 和 wordcloud 出图。这套选型的理由很直接。requests 和 BeautifulSoup 是 python 爬虫的标配文档多、报错信息容易搜jieba 对中文分词的工程化支持最成熟自定义词典、停用词过滤都很方便SnowNLP 虽然模型老但它基于电商评论语料训练拿来跑舆情文本需要重新标定阈值这个“重新标定”的过程本身就值得写进大作业文档里。主题聚类用 gensim 的 LDA跑得快参数也就几个适合课程项目如果语料超过几万条再考虑 BERTopic。可视化层有个容易忽略的点pyecharts 出的是 HTML 交互图方便答辩时现场操作wordcloud 出的是静态图适合贴在报告里。我的经验是两类图都要出交互图证明系统能看动态变化静态词云证明你有明确的数据结论。具体版本号不用追新锁在你本机 python 环境能装上的稳定版本即可。2.3 源码目录与“文档说明”怎么组织答辩不心虚拿到这类 zip 包很多人第一个动作是解压后直接在根目录乱放一堆文件。但一个可以交作业的系统目录结构要从一开始就克制。常见做法是分四个目录code、data、output、docs再放一个根 README。code 里按模块拆脚本data 里放原始采集数据和清洗后数据output 放图表和结果文件docs 放设计说明和答辩演示大纲。network_opinion_analysis/ ├── README.md ├── code/ │ ├── 01_crawler.py # 数据采集 │ ├── 02_preprocess.py # 清洗分词 │ ├── 03_sentiment.py # 情感分析 │ ├── 04_topic.py # 主题聚类 │ └── 05_visualization.py # 可视化出图 ├── data/ │ ├── raw/ # 原始数据 │ ├── clean/ # 清洗后的数据 │ └── userdict.txt # 自定义词典 ├── output/ │ ├── 词云图.png │ ├── 情感趋势.html │ └── topic_result.csv └── docs/ ├── 设计说明书.md └── 答辩演示要点.md这段目录设计想说明的不是文件该叫什么名字而是每个模块的边界。01_crawler.py 只负责把网页内容保存成 CSV不负责分词02_preprocess.py 只负责清洗和分词不负责情感打分。这样你在写文档说明时能按模块写上“该模块输入是什么、输出是什么、涉及的参数有哪些”评委老师最吃这一套。数据文件命名也建议带上时间和来源例如20250101_news_sina_raw.csv。很多作业数据量不大但文件名乱写会导致分析阶段搞不清哪份数据是清洗过的最后发现图表数据源对不上这是最常见的返工原因。文档说明不用写得像论文那么长一个设计说明加一个使用说明加起来 3000 字以内把模块流程、参数配置、运行步骤写清楚就够了。3. 把舆情分析跑起来采集、清洗、情感判断与可视化输出3.1 爬虫采集模块的最小可用脚本requests BeautifulSoup采集是整个系统最容易“翻车”也最需要控制尺度的环节。先声明一个原则课程作业请选择允许访问的公开页面控制抓取频率不要绕过任何反爬机制如果你的题目指定了某个平台优先看该平台是否有公开 API 或开放数据接口。下面这段代码是通用静态列表页的抓取模板目标 URL 需要替换成你自己选中的合规数据源。# code/01_crawler.py # 仅用于课程作业的公开信息采集示例静态新闻列表页 import time import random import requests from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) , } session requests.Session() session.headers.update(HEADERS) def fetch_page(url, retry3): for attempt in range(retry): try: resp session.get(url, timeout10) if resp.status_code 200: return resp.text except requests.RequestException as e: print(f第 {attempt 1} 次请求失败: {e}) time.sleep(2 * (attempt 1)) return None def parse_list(html): soup BeautifulSoup(html, html.parser) items [] for node in soup.select(div.news-item h2 a): items.append({ title: node.get_text(stripTrue), url: node[href], source: example_news, }) return items if __name__ __main__: base_url https://example.com/news?page all_items [] for page in range(1, 6): # 只抓前 5 页控制规模 html fetch_page(base_url str(page)) if html: all_items.extend(parse_list(html)) time.sleep(random.uniform(1.0, 2.5)) # 随机延时 print(f采集到 {len(all_items)} 条数据)这段代码的逻辑分三层fetch_page负责网络请求和重试parse_list负责从 HTML 里提取标题和链接主循环控制页码并做随机延时。三个地方需要按你的数据源调整HTML 选择器、分页 URL 规则、延时区间。timeout10防止某个请求长时间卡死整个流程retry3只在网络异常时重试不是状态码异常时重试random.uniform(1.0, 2.5)的意义是让请求间隔不均匀避免触发最简单的频率限制。如果你要抓的数据源是动态渲染页面requests 拿不到列表内容常见做法是换成 DrissionPage 或 Selenium 驱动浏览器获取渲染后 HTML但那样会慢很多课程作业不优先推荐。3.2 文本清洗与 jieba 分词停用词表、自定义词典和去重采集下来的文本不能直接丢给模型。舆情文本里常见噪声包括HTML 实体、URL、话题标签、 用户名、广告词、重复转发内容。清洗的目标不是把文本删得只剩名词而是保留能表达情绪和主题的信息同时去掉明显无意义的内容。# code/02_preprocess.py import re import jieba # 停用词表每行一个词支持 # 注释 def load_stopwords(pathdata/stopwords.txt): stopwords set() with open(path, encodingutf-8) as f: for line in f: word line.strip() if word and not word.startswith(#): stopwords.add(word) return stopwords STOPWORDS load_stopwords() def clean_text(text): if not text: return text re.sub(rhttp\S, , text) # 去掉链接 text re.sub(r#\S#, , text) # 去掉话题标签 text re.sub(r[\w-], , text) # 去掉 用户名 text re.sub(r\s, , text) # 合并空白 return text.strip() def tokenize(text): words jieba.lcut(clean_text(text)) result [] for w in words: w w.strip() if len(w) 1 and w not in STOPWORDS: result.append(w) return result jieba.load_userdict(data/userdict.txt) # 加载领域词典 if __name__ __main__: sample 【最新】某品牌发布新款旗舰手机预约量超出预期市场反应热烈 print(/.join(tokenize(sample)))清洗先于分词顺序不能反。先用正则去掉链接和话题标签可以避免 jieba 把 URL 里的字母数字切成一堆无意义 token。len(w) 1过滤单字是为了去掉“的、了、吗”这类即使不在停用词表里也基本没信息量的字但要注意“车”“房”这种单字在特定舆情里是有意义的所以这条规则要结合你的语料看是否保留。jieba.load_userdict是中文分词最重要的调参入口。舆情语料里大量出现人名、产品名、新词比如“苹果手机”“新能源汽车”“双减政策”默认词库切得不对后续情感分析会跟着错。userdict.txt 的格式是每行一个词最多三列词语、词频、词性。你不确定词频就只写字jieba 会自行判断。停用词表建议直接用哈工大停用词表做底再自己追加当前语料里出现的高频噪声词。3.3 情感分析与热度计算SnowNLP 阈值标定和分数落库情感分析模块是这个系统的“人工智能”担当但它的实现难度比想象中低。最省事的方式是用 SnowNLP 的情感分数接口它返回 0 到 1 之间的值越接近 1 越正向。问题是 SnowNLP 基于电商语料训练直接拿默认的 0.5 分界线跑舆情文本会把很多句子判偏。我建议你从清洗好的语料里抽 300 到 500 条人工打标成“正面、中性、负面”三类然后观察分数分布再定阈值。# code/03_sentiment.py from snownlp import SnowNLP # 阈值需要用自己的标注集重新标定不要照抄 THRESHOLD_POS 0.65 THRESHOLD_NEG 0.35 def get_sentiment(text): score SnowNLP(text).sentiments if score THRESHOLD_POS: label 正面 elif score THRESHOLD_NEG: label 负面 else: label 中性 return score, label if __name__ __main__: cases [ 产品发布会圆满成功现场气氛热烈, 小区附近工地夜间施工噪音扰民居民多次投诉未果, 关于该话题的讨论仍在持续各方观点不一, ] for c in cases: score, label get_sentiment(c) print(f{score:.3f} - {label} | {c})这段代码的核心不是调用 SnowNLP而是“阈值标定”这一步。你把自己标注好的样本批量跑一遍 scores画出直方图正面样本集中在 0.8 以上负面样本集中在 0.2 以下中间有明显的低谷区那阈值就设在低谷区如果样本分数重叠严重说明当前模型不适合这批语料要换训练数据。热度计算的常见做法是当天的舆情热度 当天提及量 × 正面/负面情绪加权系数。比如“正面”“中性”“负面”分别按 1、1.2、1.5 加权因为负面舆情通常传播更快。这个公式不用追求严谨但要能在文档里说清楚你为什么要这么加权。情感分析结果务必落库输出成 CSV列名建议是text, score, label, date, source后续可视化直接读这份文件。3.4 词云、趋势线、饼图与报告导出展示层决定了大作业的印象分。我的建议是出三张图词云反映热点关键词情感趋势折线图反映舆情随时间的演变情感占比饼图反映整体态度结构。词云有一个中文环境特有的坑必须指定字体路径否则中文全部显示成方块。# code/05_visualization.py from wordcloud import WordCloud import matplotlib.pyplot as plt # 如果是 Windows常见字体路径是 C:/Windows/Fonts/simhei.ttf # 如果是 macOS常见路径是 /System/Library/Fonts/PingFang.ttc font_path C:/Windows/Fonts/simhei.ttf def draw_wordcloud(words, filenameoutput/词云图.png): wc WordCloud( font_pathfont_path, width1200, height800, max_words100, # 最多显示 100 个词 background_colorwhite, # 白底黑字打印不费墨 collocationsFalse, # 避免出现重复词对 ).generate( .join(words)) wc.to_file(filename)max_words100是词云最值得调的参数。词太少显得单薄词太多全是小词。一般根据你的语料量先从 50 起步看到核心词仍然突出再往上加到 100。collocationsFalse必须开启它禁止把相邻词自动组合成短语否则会出现大量无意义的二连词占用词频位置。趋势折线图我推荐用 pyecharts 生成 HTML 版本因为可以鼠标悬浮看细节答辩效果好。情感倾向随时间的变化按天聚合横轴日期纵轴正面、中性、负面各自的发文量画三条线。最后把三张图组合进一个简单报告里输出 HTML 报告比给老师一堆散落图片显得专业得多。4. 网络舆情分析系统实战避坑现象、原因与解决4.1 现象爬虫跑着跑着被平台限流你可能会遇到脚本前几十个页面抓得顺利突然连续报超时或 403再重启已经抓不动了。这不是你的代码写错了而是请求频率触发了服务端的限流策略。很多静态页面虽然没有登录墙但会在几十分钟内统计单 IP 请求频率。原因有三个层次延时太短或固定间隔导致请求模式像机器没有保持 session 和 cookie每次都被当新访客User-Agent 太老或太通用被识别为脚本。解决方法是三管齐下用 1 到 3 秒随机延时替代固定 1 秒全程复用同一个 requests.Session让它自动保存 cookie准备 3 到 5 个不同的 User-Agent 轮换使用哪怕只是微调版本号。最稳妥的兜底方案是把抓取改成“断点续爬”。每次保存已抓 URL 到本地文件重新运行时先读这个文件做去重这样即使中间断掉也不用从头抓。这条经验不止用于舆情作业任何课程爬虫都适用。4.2 现象情感分析结果整体偏离正面文本被判负面典型场景是你抓了一轮“某产品价格波动”的新闻人工读着明明是中立报道系统却大量输出负面。我见过最离谱的一次连“市场份额创新高”都被判负面。原因一句话就能讲清SnowNLP 是在电商评论文本上训练的它理解的“正面”更多是“质量好、物流快、性价比高”而舆情文本里大量的“增长、创新、超出预期”并不在它的正向词空间里。解决分两步。第一步抽 300 到 500 条文本人工打标签跑出每条的 sentiment 分数画出正负面样本的分布图重新定阈值。如果正面样本和负面样本的分数区间几乎没有分离进入第二步不要再死磕 SnowNLP改用 TF-IDF 加朴素贝叶斯在你自己的标注集上重新训练一个情感分类器。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.pipeline import make_pipeline # texts: 清洗后的文本列表, labels: 0负面 1中性 2正面 model make_pipeline(TfidfVectorizer(ngram_range(1, 2)), MultinomialNB(alpha0.5)) model.fit(texts, labels)ngram_range(1, 2)表示同时考虑单个词和相邻两个词这对情绪表达很重要比如“不够好”和“好”如果只按单字词语义就扭了。alpha0.5是拉普拉斯平滑系数数值越小越依赖训练数据标注集超过 500 条时设 0.5 比较稳妥少于 200 条就设 1.0 防过拟合。这个模型训练快参数也好解释放到大作业文档里比 SnowNLP 更拿得出手。4.3 现象主题聚类数量选不准每个主题都很碎LDA 主题聚类输入的num_topics是 K 值很多人在 3 到 10 之间反复试效果都是要么主题太粗、五六类都混在一起要么太碎、一类事件被拆成三四个。原因是对舆情语料来说K 值的合理区间跟事件覆盖度相关你只分析一个热点事件K 设 3 到 5 足够你分析一个月内某行业全部舆情K 至少要 8 到 12。解决方式是用困惑度或主题一致性做粗略筛选。简单做法是跑一组 K 值比如 4、6、8、10每轮计算 LDA 模型的 coherence选一个开始平坦化的拐点。但课程作业不用那么学术我一般建议直接人工检查两三轮。先把 K 设成 6跑出来看每个主题下前 10 个词如果多个主题的前 10 词高度重叠就调大 K如果某个主题里同时出现两个明显无关的事件名就调小 K。LDA 还需要注意随机性问题。同一份语料、同一个 K 值跑两次主题词可能不一样。解决方法是设置random_state42让结果可复现这个细节写进文档能加不少分。4.4 现象采集量看着吓人清洗后没剩多少你可能遇到脚本显示采集了 5 万条按标题去重后只剩 8000 条再按内容清洗后只剩 4000 条最后能用于分析的只有 3000 条。这不一定是坏事但说明采集阶段没有做初步过滤把大量重复转发、短文案、广告内容抓了进来。原因通常有两个。一是同一个新闻被多个站点转载标题都一模一样二是平台自己的转发和滚动更新机制让同一条内容反复出现。解决方法是加两级去重第一级按 URL 去重第二级按标题的哈希值去重。二级去重时建议对标题先做简单归一化去掉标点符号和空格否则“某某事件最新进展”和“某某事件最新进展”会算成两条。import hashlib def title_hash(title): norm re.sub(r[^\w\u4e00-\u9fa5], , title) return hashlib.md5(norm.encode(utf-8)).hexdigest() seen set() def is_duplicate(title): h title_hash(title) if h in seen: return True seen.add(h) return False这个哈希去重比直接在 DataFrame 里用drop_duplicates快得多几万条数据毫秒级完成。需要说明的是去重只解决“同文不同源”的问题不解决“同一事件的不同报道”问题后者应该交给 LDA 主题聚类去归类而不是在预处理层强行删除。5. 从“跑通”到“作品”验证方法、模块封装与交付自查5.1 三组固定测试集让每次运行都有可比性舆情分析系统最容易被问倒的一个问题是“你凭什么说这个结果是对的”这不是让你证明算法精度有多高而是要建立一套可验证的基准。我习惯在处理好的数据里保留三组固定测试集一组 100 条纯新闻标题一组 100 条带明显情绪倾向的短评一组 100 条包含新词、方言词或反讽表达的疑难文本。这三组数据不参与模型训练只用来做回归验证。每次改动情感阈值、停用词表或分词词典后就跑一遍三组测试集记录正面、中性、负面的命中比例。只要三条数据线的命中率没有异常波动就可以放心继续。这套办法不需要你懂多少机器学习评估理论但它能让你在答辩时说出“我在清洗后保留了三组基准数据每个模块调整后都做回归测试”这比“模型准确率 95%”可信得多因为在数据量有限的情况下准确率本身没什么说服力。5.2 配置与数据路径外置临时换主题不慌课程大作业有个常见意外老师看完初稿说“你能不能换一个热点事件重跑一遍”。如果你的代码里写死了数据文件名、阈值、字体路径改一个变量要动五个脚本一晚上就耗进去了。我的习惯是把所有可变参数集中到一个config.py或 YAML 文件里。# code/config.py DATA_DIR data/raw OUTPUT_DIR output STOPWORDS_FILE data/stopwords.txt USERDICT_FILE data/userdict.txt FONT_PATH C:/Windows/Fonts/simhei.ttf THRESHOLD_POS 0.65 THRESHOLD_NEG 0.35 TOP_N_WORDS 100 TOPIC_K 6其他脚本统一import config不要自己在文件里再写一遍路径。这个习惯最大的好处是换数据源时只需要把新 CSV 放进data/raw改DATA_DIR指向它重跑整条链路即可。做舆情分析的人都知道数据一换分词词典、停用词列表、情感阈值都要跟着微调配置集中化能让你在半小时内完成一次全流程重跑。5.3 答辩交付前我最后两小时一定会做的自查清单最后这部分是我自己的交付习惯每次交大作业或项目前都按这个顺序过一遍。第一从头跑一遍完整的五段式流程确认没有改路径导致的 FileNotFoundError第二检查三张图的字体和标签有没有乱码尤其是换过机器以后中文字体路径经常失效第三打开输出 CSV看情感标签列是否有空值这往往是清洗阶段某条文本为空导致的第四在干净环境里用pip install -r requirements.txt装依赖确认没有遗漏某个库第五把 README 里的运行命令照着敲一遍确保别人能复现。依赖清单这个环节最容易出问题。很多人代码里用了from collections.abc import Iterable但 requirements 里只写了pandas没写具体版本换一台 python 3.9 的机器直接报错。锁依赖版本时不必追求最新只要记录你当前环境实测能跑过的版本即可并在 README 里注明建议的 python 版本。我习惯把pip freeze requirements.txt生成的完整列表提交到 docs 目录虽然列表长但保证复现不出问题。最后再说一个血泪经验交付前一定要在另一台电脑或虚拟环境里完整跑一遍。你本机能跑不代表代码没问题有时纯粹是某个依赖包版本碰巧兼容。网络舆情分析系统本身不复杂但它依赖链挺长——requests、beautifulsoup4、jieba、snownlp、gensim、wordcloud、pyecharts——任何一个版本不兼容都会在中途报错。提前花一个小时做干净环境验证比答辩当天翻车强太多。希望这篇笔记能帮你把这个经典的人工智能大作业做扎实少走我当年走过的弯路。本文还有配套的精品资源点击获取
返回列表