Python数据驱动项目实战:疫情数据与微博情感关联分析系统 📅 发布时间:2026/9/3 8:30:20 👁 浏览次数: 简介本资源是一套完整的Python数据工程实践项目面向计算机、数学、电子信息等专业的本科生及初学者聚焦疫情数据与社交媒体舆情的采集、治理与分析全流程。项目涵盖疫情实时数据爬取、微博关键词定向抓取含MySQL数据库存储、结构化数据清洗与特征构建、多维度可视化呈现含疫情趋势图与词云以及基于情感词典的微博文本细粒度情感分析适合作为课程设计、期末大作业或毕业设计参考。压缩包共15个文件含7个核心Python脚本爬虫、预处理、可视化、情感分析模块、3个CSV数据集含标注样本与情感关键词库、2个文本词典正/负向词表、1个JSON微博原始数据、1张结果示意图及1份Markdown项目说明文档整体大小23.87MB。已有728人学习下载提供开箱即用的完整代码链路、清晰的模块划分与可复现的数据分析流程便于理解数据从采集到价值输出的闭环逻辑。1. 项目概述一个数据驱动的疫情社会观察系统最近在整理硬盘翻出来一个前两年做的老项目当时主要是想看看社交媒体上的公众情绪和官方发布的疫情数据之间有没有什么有趣的关联。这个项目麻雀虽小五脏俱全从数据抓取、清洗、存储到分析和可视化走完了一个完整的数据分析闭环。它不是简单的“爬虫可视化”的堆砌核心在于将结构化的疫情统计数据与非结构化的社交媒体文本进行关联分析试图从数据层面理解特定时期的社会心态。简单来说这个系统干了三件事第一从权威数据源定时抓取结构化的疫情数据如新增确诊、累计治愈等第二同步从微博抓取与疫情相关的关键词下的实时讨论内容第三将这两类数据清洗后存入数据库并进行情感倾向分析最后通过可视化图表呈现数据趋势与公众情绪的对比。这非常适合想通过一个实战项目系统学习Python数据获取、处理、分析和呈现全流程的朋友尤其是对社会计算或舆情分析感兴趣的同学。2. 项目整体架构与技术选型解析2.1 核心模块拆解与设计思路整个项目可以清晰地划分为四个核心模块它们之间通过数据库进行数据流转形成一个松耦合但逻辑紧密的管道。数据采集层这是项目的“触手”。我将其设计为两个独立的爬虫但共享同一套请求管理和异常处理机制。疫情数据爬虫目标是各级卫健委官网或聚合数据平台。这类数据通常以JSON接口或规整的HTML表格形式存在变化频率低每日更新但要求数据准确、稳定。因此这个爬虫的核心是稳健性需要处理好反爬策略如请求头、频率限制并设计重试和验证逻辑确保抓取到的每个数字都是可靠的。微博关键词爬虫目标是微博的搜索页面或移动端接口。微博数据是非结构化的文本、时间、用户信息混合体且反爬严密动态参数、登录态、滑块验证。这个爬虫的核心是突破性与效率。我选择模拟移动端请求并利用关键词轮询持续抓取最新发布的微博。这里的关键在于如何稳定地获取数据而不是一味求快。注意微博爬虫的合规性是红线。本项目代码仅用于学习网络请求解析、动态页面处理和数据抽取技术。实际应用中必须严格遵守robots.txt协议控制请求频率避免对目标服务器造成压力且所有数据仅应用于个人学习与研究分析。数据存储层选用关系型数据库MySQL作为数据仓库。为什么不用更简单的CSV或NoSQL因为我们需要处理两类差异很大的数据并且后续需要频繁地进行关联查询和聚合分析。表结构设计epidemic_data表存储日期、地区、新增确诊、新增疑似、累计确诊、治愈、死亡等字段。以(date, region)作为复合主键。weibo_data表存储微博ID、发布时间、用户昵称、微博正文、关键词、抓取时间等。最重要的是我增加了一个cleaned_text字段用于存放预处理后的纯文本为情感分析做准备。这种设计便于我们执行诸如“查询某日全国新增确诊数并关联分析当天‘隔离’关键词下的微博情感倾向”这样的操作。数据处理与分析层这是项目的“大脑”。数据预处理这是最繁琐但决定分析质量的一步。对于疫情数据主要是处理缺失值、异常值如某日数据为负。对于微博文本则复杂得多去除广告、表情符号、URL链接进行中文分词去除停用词如“的”、“了”文本规范化。情感分析我采用了基于预训练模型如SnowNLP或百度AI开放平台的情感倾向分析API的方法。SnowNLP适合离线、大批量处理虽然精度不是最高但一致性较好。将微博cleaned_text字段的内容送入模型得到情感极性分值如0-1越接近1越积极存入数据库的新字段sentiment_score中。数据可视化层使用MatplotlibSeaborn库目标是生成能清晰对比趋势的图表。疫情数据通常用折线图展示新增/累计趋势用堆叠面积图展示确诊、治愈、死亡病例的构成变化。微博情感数据可以用每日情感平均分折线图来观察情绪波动用热力图展示不同关键词在不同时间段的情感强度。最关键的是将两条折线例如“当日新增确诊数”和“当日‘疫情’关键词微博平均情感分”放在同一个坐标系中观察它们的走势关联这是整个项目视觉上的亮点。2.2 关键技术栈与工具选型理由Python 3.8: 生态丰富是数据科学和爬虫领域的事实标准。Requests BeautifulSoup4: 用于疫情数据爬取和静态页面解析。Requests简洁高效BeautifulSoup对新手友好足以应对大多数政府公开数据页面。Selenium (可选): 作为微博爬虫的备选方案。当接口逆向工程困难时可以用Selenium模拟浏览器行为直接获取渲染后的数据缺点是速度慢、资源消耗大。本项目核心是学习因此我同时提供了Requests逆向和Selenium两种思路的代码注释。PyMySQL / SQLAlchemy: 用于连接和操作MySQL数据库。PyMySQL更直接SQLAlchemy的ORM模式能让代码更优雅适合中型项目。Pandas NumPy: 数据预处理的绝对核心。Pandas的DataFrame是处理表格数据的利器清洗、转换、聚合操作都非常方便。Jieba: 最好的中文分词Python库之一用于对微博正文进行分词。SnowNLP / TextBlob: 轻量级的情感分析库。SnowNLP针对中文优化开箱即用适合学习入门。Matplotlib Seaborn:Matplotlib是基础绘图库功能强大但配置繁琐Seaborn基于前者提供了更美观的统计图表样式和高级接口两者结合使用效率最高。Schedule / APScheduler: 用于实现爬虫的定时任务让数据采集自动化运行。3. 核心模块实现细节与实操要点3.1 双爬虫的稳健实现与防封禁策略疫情数据爬虫的实现 关键在于找到稳定、结构化的数据源。我以某数据平台的公开API为例。import requests import pandas as pd import time def fetch_epidemic_data(date_str): 获取指定日期的疫情数据 url fhttps://api.example.com/epidemic/data # 示例URL实际需替换 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..., Accept: application/json } params {date: date_str} try: # 添加延迟避免请求过快 time.sleep(1) resp requests.get(url, headersheaders, paramsparams, timeout10) resp.raise_for_status() # 检查HTTP错误 data resp.json() # 解析JSON转换为规整的列表 records [] for item in data[results]: record { date: item[date], region: item[region][name], confirmed_new: item[confirmed][new], confirmed_total: item[confirmed][total], cured_new: item[cured][new], # ... 其他字段 } records.append(record) return pd.DataFrame(records) except requests.exceptions.RequestException as e: print(f请求失败: {e}) return pd.DataFrame() # 返回空DataFrame后续统一处理实操心得对于公开API务必仔细阅读其文档了解调用频率限制。将请求头User-Agent设置为常见的浏览器标识并合理使用time.sleep()是基本的礼貌。所有网络请求都必须包裹在try-except块中并记录日志这是保证爬虫长期稳定运行的基础。微博关键词爬虫的实现 这里以通过解析移动端接口为例难度较高。import requests import re import json def fetch_weibo_by_keyword(keyword, max_page5): 通过关键词搜索微博模拟移动端接口 base_url https://m.weibo.cn/api/container/getIndex headers { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15..., Referer: fhttps://m.weibo.cn/search?keyword{keyword} } weibo_list [] for page in range(1, max_page1): params { containerid: f100103type1q{keyword}, page_type: searchall, page: page } try: resp requests.get(base_url, headersheaders, paramsparams, timeout15) data resp.json() # 解析cards中的微博信息 if data.get(ok) 1: cards data.get(data, {}).get(cards, []) for card in cards: mblog card.get(mblog) if mblog: weibo_info { id: mblog.get(id), text: mblog.get(text), created_at: mblog.get(created_at), user_name: mblog.get(user, {}).get(screen_name), keyword: keyword } # 简单清洗HTML标签 weibo_info[text] re.sub(r[^], , weibo_info[text]) weibo_list.append(weibo_info) time.sleep(2) # 非常重要页间延迟 except Exception as e: print(f抓取关键词{keyword}第{page}页失败: {e}) break return weibo_list核心技巧微博的反爬策略会经常变动。containerid等参数可能需要通过浏览器开发者工具抓包分析获取。User-Agent必须设置为移动端。页间延迟(time.sleep)是避免IP被临时封禁的最有效手段之一不要贪快。此外可以考虑使用IP代理池来应对更严格的限制但这超出了基础学习的范围。3.2 数据库设计与高效数据入库我使用SQLAlchemy来定义数据模型这样代码更清晰也便于维护。from sqlalchemy import create_engine, Column, String, Integer, Float, Date, Text from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base declarative_base() class EpidemicData(Base): __tablename__ epidemic_data id Column(Integer, primary_keyTrue, autoincrementTrue) date Column(Date, nullableFalse) region Column(String(50), nullableFalse) confirmed_new Column(Integer) confirmed_total Column(Integer) cured_new Column(Integer) # ... 其他字段 # 设置复合唯一约束防止重复插入同一天同一地区的数据 __table_args__ (UniqueConstraint(date, region, nameuix_date_region),) class WeiboData(Base): __tablename__ weibo_data id Column(Integer, primary_keyTrue, autoincrementTrue) weibo_id Column(String(30), uniqueTrue, nullableFalse) # 微博唯一ID created_at Column(String(30)) user_name Column(String(100)) raw_text Column(Text) # 原始文本 cleaned_text Column(Text) # 清洗后文本 keyword Column(String(50)) sentiment_score Column(Float) # 情感分析得分 crawl_time Column(Date) # 抓取时间 # 数据库连接与会话 engine create_engine(mysqlpymysql://user:passwordlocalhost:3306/epidemic_analysis?charsetutf8mb4) Base.metadata.create_all(engine) # 创建表 Session sessionmaker(bindengine)注意事项字符集微博文本包含大量Emoji和特殊符号MySQL的utf8mb4字符集是必须的标准的utf8无法存储四字节的Emoji。唯一约束为微博ID设置UNIQUE约束可以避免在多次抓取中插入重复数据。疫情数据表则通过(date, region)的复合唯一约束来去重。批量插入无论是Pandas的to_sql方法设置if_existsappend还是使用SQLAlchemy的session.bulk_save_objects()都比逐条插入session.add()快一个数量级。处理爬虫数据时务必采用批量操作。3.3 数据预处理的实战流程数据预处理在单独的脚本中完成它从数据库读取原始数据处理后再写回或存入新表。疫情数据清洗import pandas as pd from sqlalchemy import create_engine engine create_engine(mysqlpymysql://...) df_epidemic pd.read_sql(SELECT * FROM epidemic_data WHERE date 2022-01-01, engine) # 1. 处理缺失值对于数值字段用前向填充或插值法 df_epidemic[confirmed_new].fillna(methodffill, inplaceTrue) # 2. 处理异常值假设新增确诊不应为负将其置为0或标记 df_epidemic.loc[df_epidemic[confirmed_new] 0, confirmed_new] 0 # 3. 确保日期格式 df_epidemic[date] pd.to_datetime(df_epidemic[date]) # 4. 去重基于数据库约束这里作为二次保险 df_epidemic.drop_duplicates(subset[date, region], keepfirst, inplaceTrue)微博文本清洗与情感分析 这是更核心的部分。import re import jieba from snownlp import SnowNLP import numpy as np def clean_weibo_text(text): 清洗单条微博文本 if not isinstance(text, str): return # 去除网页标签、URL、用户、话题符号 text re.sub(r[^], , text) text re.sub(rhttp[s]?://\S, , text) text re.sub(r[\w\u4e00-\u9fa5], , text) text re.sub(r#([^#])#, r\1, text) # 保留话题内容去掉#号 # 去除Emoji和特殊符号简单版 text re.sub(r[^\w\u4e00-\u9fa5。、“”‘’《》【】\s], , text) # 分词并用空格连接SnowNLP可直接处理字符串但分词后效果更好 words jieba.lcut(text) cleaned .join([w for w in words if w.strip()]) return cleaned def analyze_sentiment_batch(text_list): 批量情感分析返回分数列表 scores [] for text in text_list: if text and len(text.strip()) 5: # 过滤过短文本 try: s SnowNLP(text) scores.append(s.sentiments) # 情感积极度0-1 except: scores.append(0.5) # 分析失败取中性值 else: scores.append(0.5) # 无意义文本取中性值 return scores # 从数据库读取原始微博数据 df_weibo pd.read_sql(SELECT id, raw_text FROM weibo_data WHERE cleaned_text IS NULL, engine) # 应用清洗函数 df_weibo[cleaned_text] df_weibo[raw_text].apply(clean_weibo_text) # 批量情感分析 texts_for_analysis df_weibo[cleaned_text].tolist() sentiment_scores analyze_sentiment_batch(texts_for_analysis) df_weibo[sentiment_score] sentiment_scores # 将清洗后的数据和情感分写回数据库 # ... (使用df_weibo.to_sql或ORM更新对应记录)经验之谈文本清洗没有“标准答案”。你需要根据分析目标调整清洗强度。例如如果关心用户情绪保留表情符号可能更有价值需要专门的表情分析。SnowNLP的情感分析对于长文本和正式文本效果尚可但对于微博短文本、网络流行语、反讽句的识别能力有限。这是所有情感分析工具的共性局限在解读结果时必须心中有数。4. 数据可视化让趋势与关联一目了然可视化不是为了画图而画图而是为了回答问题。我们的核心问题是疫情数据的变化与公众情绪波动有关联吗4.1 疫情趋势可视化首先我们绘制疫情本身的发展趋势。import matplotlib.pyplot as plt import seaborn as sns sns.set_style(whitegrid) # 设置Seaborn样式 plt.rcParams[font.sans-serif] [SimHei] # 解决中文显示问题 plt.rcParams[axes.unicode_minus] False # 假设我们已经有一个按日期聚合的疫情DataFrame: df_epidemic_daily fig, axes plt.subplots(2, 1, figsize(14, 10)) # 子图1新增确诊与新增治愈的对比折线图 axes[0].plot(df_epidemic_daily[date], df_epidemic_daily[confirmed_new], label新增确诊, colorcoral, linewidth2) axes[0].plot(df_epidemic_daily[date], df_epidemic_daily[cured_new], label新增治愈, colorlightgreen, linewidth2) axes[0].set_title(每日新增确诊与新增治愈趋势对比) axes[0].set_xlabel(日期) axes[0].set_ylabel(人数) axes[0].legend() axes[0].fill_between(df_epidemic_daily[date], 0, df_epidemic_daily[confirmed_new], colorcoral, alpha0.3) # 子图2累计确诊、治愈、死亡的堆叠面积图 axes[1].stackplot(df_epidemic_daily[date], df_epidemic_daily[confirmed_total], df_epidemic_daily[cured_total], df_epidemic_daily[dead_total], labels[累计确诊, 累计治愈, 累计死亡], colors[lightcoral, lightgreen, gray]) axes[1].set_title(累计病例构成变化) axes[1].set_xlabel(日期) axes[1].set_ylabel(累计人数) axes[1].legend(locupper left) plt.tight_layout() plt.savefig(epidemic_trend.png, dpi300, bbox_inchestight) plt.show()4.2 微博情感趋势与关联分析接下来分析微博情绪并尝试与疫情数据关联。# 从数据库读取按日期和关键词聚合的情感平均分 df_sentiment_daily pd.read_sql( SELECT DATE(created_at) as date, keyword, AVG(sentiment_score) as avg_sentiment FROM weibo_data WHERE sentiment_score IS NOT NULL GROUP BY DATE(created_at), keyword ORDER BY date , engine) # 假设我们关注“疫情”这个关键词 df_keyword df_sentiment_daily[df_sentiment_daily[keyword] 疫情].copy() # 合并疫情数据与情感数据按日期连接 df_merged pd.merge(df_epidemic_daily, df_keyword, ondate, howinner) fig, ax1 plt.subplots(figsize(15, 7)) color tab:red ax1.set_xlabel(日期) ax1.set_ylabel(新增确诊数, colorcolor) line1 ax1.plot(df_merged[date], df_merged[confirmed_new], colorcolor, label新增确诊, linewidth2) ax1.tick_params(axisy, labelcolorcolor) # 创建第二个Y轴共享同一个X轴 ax2 ax1.twinx() color tab:blue ax2.set_ylabel(微博情感平均分, colorcolor) line2 ax2.plot(df_merged[date], df_merged[avg_sentiment], colorcolor, label情感分(疫情关键词), linestyle--) ax2.tick_params(axisy, labelcolorcolor) # 情感分范围是0-1可以固定Y轴范围以便观察波动 ax2.set_ylim(0, 1) # 添加图例 lines line1 line2 labels [l.get_label() for l in lines] ax1.legend(lines, labels, locupper left) plt.title(新增确诊趋势与“疫情”关键词微博情感趋势对比) fig.tight_layout() plt.savefig(correlation_analysis.png, dpi300) plt.show()通过这张双Y轴图我们可以直观地观察两条曲线的走势。例如可能发现在新增确诊数大幅攀升后的几天微博情感平均分会出现一个明显的低谷情绪转向消极。这只是一个粗略的观察严谨的相关性分析还需要计算统计指标如皮尔逊相关系数。5. 项目部署、优化与常见问题排查5.1 工程化与自动化部署一个学习项目不能只停留在Jupyter Notebook里。我将其模块化并编写了主调度脚本。spider_epidemic.py: 疫情数据爬虫模块。spider_weibo.py: 微博爬虫模块。data_processor.py: 数据清洗与情感分析模块。visualizer.py: 可视化绘图模块。config.py: 配置文件存放数据库连接信息、API密钥如有、关键词列表等。main_scheduler.py: 主调度脚本使用schedule库定时运行任务。# main_scheduler.py 示例 import schedule import time from spider_epidemic import main as run_epidemic_spider from spider_weibo import main as run_weibo_spider from data_processor import main as run_data_processor def job(): print(开始每日数据任务...) run_epidemic_spider() # 抓取昨日疫情数据 run_weibo_spider() # 抓取最新微博 run_data_processor() # 处理新数据并分析 print(每日数据任务完成。) # 每天上午9点执行 schedule.every().day.at(09:00).do(job) while True: schedule.run_pending() time.sleep(60)可以将这个脚本放在服务器上用nohup或systemd在后台运行实现全自动化数据流水线。5.2 性能优化与扩展思路异步爬虫对于微博这类需要大量I/O等待的爬虫可以使用aiohttpasyncio实现异步并发极大提升抓取效率。但要注意目标网站的压力控制并发数。增量爬取微博爬虫不应每次都从头翻页。可以记录上次抓取到的最新微博ID下次请求时只抓取比这个ID更新的数据。情感分析升级SnowNLP是基线方案。可以尝试使用更强大的预训练模型如BERT在自己的标注数据上微调以获得更精准的情感分类积极、消极、中性甚至情绪识别愤怒、恐惧、悲伤等。前端展示用Flask或Django搭建一个简单的Web应用使用ECharts或Plotly库生成交互式图表让分析结果可以通过网页动态查看。5.3 常见问题与排查实录在开发和运行过程中我遇到了不少坑这里记录下最典型的几个问题1疫情数据爬虫突然返回空数据或403错误。排查首先检查目标网站是否改版API地址或参数是否变化。其次检查请求头User-Agent和Referer是否被屏蔽尝试更换。最后检查IP是否被临时封禁。解决更新请求头信息在代码中加入更长的随机延迟考虑使用付费的代理IP服务针对高频率抓取如果网站提供优先使用其官方数据接口或开放数据集。问题2微博爬虫运行一段时间后返回的数据全是“加载中”或需要登录。排查这是最典型的问题。微博的未登录态访问有严格限制和频率限制。通过浏览器开发者工具检查网络请求中是否包含cookies或authorization等认证信息。解决方案A简单但不稳定用Selenium模拟登录获取cookies后供Requests使用。但登录可能触发滑块验证。方案B推荐学习研究微博移动端或PC端的登录接口逆向加密过程实现程序化登录获取令牌(access_token)。这涉及复杂的逆向工程是爬虫技术进阶的必经之路。忠告对于学习项目可以手动获取一次cookies放入代码中短期使用。长期运行必须考虑自动化更新认证信息的机制。问题3情感分析结果不准确很多反讽句被判断为积极。排查这是自然语言处理特别是中文短文本情感分析的普遍难题。SnowNLP的训练语料比较通用对网络用语和反讽识别能力弱。解决降低期望理解工具的局限性将分析结果视为一种“情绪热度”的粗略指标而非精确的情感判断。后处理规则建立一份“负面词”词典如果文本中包含“无语”、“离谱”等词即使模型打分高也手动调整为负面或中性。升级模型如5.2所述使用更先进的模型并在特定领域的语料上微调。问题4可视化图表中文显示为方框。排查Matplotlib默认字体不包含中文。解决代码中已给出方案。确保系统存在中文字体如SimHei黑体并通过plt.rcParams[font.sans-serif]进行设置。在Linux服务器上部署时可能需要手动安装中文字体。问题5数据库写入速度慢。排查是否在循环中逐条执行session.add()和session.commit()解决务必使用批量操作。对于Pandas用df.to_sql(..., if_existsappend, indexFalse, chunksize500)。对于SQLAlchemy ORM使用session.bulk_save_objects(list_of_objects)最后再统一session.commit()。这个项目从构思到实现几乎踩遍了数据采集和分析各个阶段的常见坑。它最大的价值不在于得出了某个惊天动地的结论而在于完整地实践了一套数据驱动的解决方案。当你亲手让散落各处的数据经过自己的代码管道最终变成一幅能讲述故事的图表时那种感觉远比单纯调用一个分析API要深刻得多。对于初学者我建议不要一开始就追求大而全可以先实现疫情数据爬取和可视化再逐步加入微博爬虫和情感分析像搭积木一样把这个系统构建起来每一步都吃透其中的原理和问题这样收获才是实实在在的。本文还有配套的精品资源点击获取