疫情数据分析可视化课设全流程:爬虫到Flask大屏实战
简介这是一份面向计算机、电子信息工程、数学等专业学生的Python课程设计/毕业设计参考资料主题为新冠肺炎疫情数据分析与可视化源自获得导师认可并取得98分的完整课设项目。资源包共57个文件核心内容以Python源码、数据文件、可视化图表和HTML交互页面为主并包含Jupyter Notebook分析记录、Markdown笔记与Word版算法设计文档整体体积3.94MB。已有424人学习参考可作为数据分析类课程设计或毕业设计的完整范例。具体内容涵盖疫情时序预测、微博评论情感分析、NLP处理、地图/词云可视化、数据爬虫等模块从数据采集、清洗、建模到可视化展示均有对应代码实现配套的算法设计文档和报告结构能帮助读者快速理解项目思路并迁移到其他数据分析场景中。对需要完成类似选题的学生而言是一份兼顾代码、数据、文档的完整参考资料。1. 一份能直接跑的疫情数据分析课设从爬虫到大屏的完整链路如果期末要交一份 Python 数据分析大作业最怕的不是代码写得不好而是东拼西凑交了三个静态图表导师一眼看出没做完整链路。最近我拆了一套标注为“疫情数据分析与可视化”的毕业设计课设资源它把数据采集、清洗、时序预测、情感分析和 Flask 可视化大屏串成了一条完整链路代码加报告都是现成的。这套资源定位很明确面向计算机、电子信息工程、数学等专业的学生做课程设计、期末大作业或毕业设计参考。数据分析的常用库、爬虫脚本、NLP 处理、ECharts 大屏、算法设计文档全在压缩包里不需要再去搜零零散散的技术片段直接照着跑就能从原始 CSV 一路看到最终可视化页面。2. 项目结构拆解从压缩包到可运行的三层架构解压这个课设压缩包之后第一反应是文件有点多但理清关系之后会发现结构非常规矩。项目根目录是 COVID-19-NLP-vis-master里面既有 Jupyter Notebook也有 Flask 服务端代码、HTML 模板、爬虫脚本和数据集。这里我先做一个整体拆解把每一类文件的职责说清楚后面再讲运行时就不会迷路。2.1 目录文件清单先分清数据、脚本与页面三块目录 / 文件职责疫情分析和预测部分.ipynb疫情数据探索、清洗、时序预测与可视化的 Notebook 全流程NLP.ipynb社交平台评论数据的 NLP 分析流程分词、TF-IDF、情感倾向templates/Flask 页面模板index.html 主大屏、analyse.html 分析页、NLP.html 舆情页、test.html 测试页scripts/核心 Python 脚本覆盖爬虫、数据处理、模型拟合、可视化数据生成dataSets/原始数据集国内外疫情数据、社交平台评论、人口统计数据等 CSVresults/脚本运行后产出的中间结果供前端页面读取server.pyFlask 服务入口提供页面渲染和 JSON 数据接口新冠肺炎时序数据预测算法设计.docx时序预测算法的设计文档对应课程报告README.html / README.md项目说明文档介绍运行方式和模块结构pyvenv.cfg / notebook虚拟环境配置与 notebook 相关文件scripts 目录是这套课设最值钱的部分。weibo_战疫情爬虫_spider.py、spider-yqkx.py、spider-社会组织.py 三个爬虫分别负责抓社交平台评论、疫情快讯和社会组织参与数据weiboProcess.py 和 mergeweibos.py 负责评论数据的清洗与合并logistic.py 做逻辑增长曲线拟合jiebafenci.py、tfidf.py、sentiments.py、weiboAnalyse.py 构成 NLP 分析链路mapchina.py、mapworld.py、lineCountry.py、hierarchy.py 是地图和趋势图的数据生成脚本wordData.py、weiboWordData.py 负责词频统计供前端词云使用。这几条线分开看都不复杂但合在一起就是一份完整的数据分析课设爬虫负责取数pandas 负责清洗模型负责预测NLP 负责文本挖掘最后统一由 Flask 把结果渲染成浏览器里可交互的大屏。模板文件里的 analyse.html、NLP.html 和 index.html 分别对应该项目的三大展示面wordcloud.js、calendar.js、weibolines.js 这些前端脚本则是 ECharts 的定制组件分别负责词云、日历热力图和舆论趋势折线。2.2 技术选型requests pandas scipy Flask ECharts 的组合逻辑这套技术栈放在课程设计里非常讨巧。数据采集层用 requests 配合 BeautifulSoup轻量直接不需要分布式爬虫那套重武器数据处理层是 pandasCSV 读入、合并、透视、分组统计都是课设报告里最好写篇幅的地方模型层用 scipy 的 curve_fit 做逻辑斯蒂增长曲线拟合配合 sklearn 的 TfidfVectorizer 做文本特征提取正好覆盖了“时序预测”和“NLP”两个加分项服务端用 Flask代码量小一个 server.py 就能把所有页面和数据接口串起来前端可视化则直接基于 ECharts用 PyECharts 生成地图 JSON 或者在前端用 ECharts 实例渲染。选型上没有引入数据库和消息队列数据的传递靠 CSV 文件落盘脚本之间通过文件接力。这种设计在工程上属于“土但稳”但对课设来说反而是优点每一步中间结果都能打开看出错了容易定位汇报时也方便展示“我处理到了哪一步”。我一般会建议做课设的同学不要盲目上重型组件文件接力能让项目更可控这也是这套资源值得参考的地方。2.3 数据流链路CSV 到浏览器页面之间发生了什么整套项目的数据流可以概括为“原始 CSV 落盘 → 脚本清洗分析 → 结果写入 results/ → server.py 读取并暴露为 JSON → 前端 fetch 渲染”。以疫情曲线页面为例脚本先把 china_provincedata.csv 拟合出预测值写入 results/china_curve_pred.csv然后 server.py 提供一个接口返回这份数据前端 index.html 里的 render.js 再拉取并绘制折线。server.py 中常见的接口写法大概是这样的import pandas as pd from flask import Flask, jsonify, render_template app Flask(__name__) app.config[JSON_AS_ASCII] False # 确保返回给前端的中文不乱码 app.route(/) def index(): return render_template(index.html) app.route(/api/china_curve) def china_curve(): df pd.read_csv(results/china_curve_pred.csv, encodingutf-8) return df.to_json(orientrecords, force_asciiFalse) if __name__ __main__: app.run(debugTrue, host127.0.0.1, port5000)这段代码有几个关键参数需要注意。JSON_AS_ASCII 设为 False 之后Flask 返回的 JSON 里中文字符会原样保留而不是被转成 \uXXXX 的 Unicode 转义序列这是很多课设在前端看到中文乱码的根源之一。to_json 方法里的 orientrecords 表示把 DataFrame 转成数组对象每条记录是一个 dict前端拿到后可以直接用 data.forEach 遍历。force_asciiFalse 与前者同理保证 CSV 里的中文列名和值正确落到 JSON 里。整个链路没有魔法入口是 CSV出口是 API中间每一步都是文件接力排查起来非常直观。3. 数据采集与预处理把爬虫和CSV洗成可分析样本数据质量决定了后面模型和可视化能做多深。这套课设在数据采集层明显下了功夫三个爬虫脚本分别对应三个不同的数据维度疫情数据、社交平台评论、社会组织信息。同时 dataSets 里既有原始评论 CSV也有世界各国疫情数据和人口数据预处理脚本要把这些来源不同、格式各异的数据统一成模型能直接吃的 DataFrame。3.1 三类爬虫脚本的分工逻辑先看爬虫部分。weibo_战疫情爬虫_spider.py 抓的是社交平台内与疫情相关的公开评论字段通常包含用户名、发布时间、评论内容、点赞数等spider-yqkx.py 抓的是每日疫情快讯对应 dataSets 里的 yqkx_data-5_21.csvspider-社会组织.py 则抓取社会组织在疫情期间发布的行动信息用于后续统计不同类型组织的参与方式。三个爬虫抓完的数据都会先落到 CSV再由 mergeweibos.py 把多个评论文件合并成一份完整数据集。爬虫的写法在课设里不需要太复杂requests 加上 BeautifulSoup 就能覆盖大多数公开页面关键是要控制请求头、请求频率和异常处理。常见做法是先把请求封装成一个函数返回解析后的结构化数据最后统一写入 CSV代码结构大概长这样import requests from bs4 import BeautifulSoup import pandas as pd import time headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://example.com/ } def fetch_page(page_no): # 目标站点通常用 page 参数做分页 url fhttps://example.com/list?page{page_no} resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) items [] for node in soup.select(.item-list .item): items.append({ content: node.select_one(.text).get_text(stripTrue), date: node.select_one(.time).get_text(stripTrue) }) return items all_data [] for page in range(1, 6): all_data.extend(fetch_page(page)) time.sleep(1) # 控制抓取频率避免被发现 pd.DataFrame(all_data).to_csv(dataSets/raw_comments_page1_5.csv, indexFalse, encodingutf-8)这里有几个细节值得注意。time.sleep(1) 是必须的没有频率控制的爬虫很容易触发目标站点的反爬机制轻则返回验证码页面重则封 IP。resp.encoding utf-8 是处理中文页面乱码的常规手段不过有些站点是 GBK 编码遇到乱码时可以先用 resp.apparent_encoding 探一下实际编码再赋回去。select 的选择器完全取决于目标页面的 HTML 结构课设里抓不到数据时第一件事就是用浏览器开发者工具确认当前页面的真实 DOM 结构而不是反复调代码。3.2 CSV 的清洗与合并把多源数据统一成一张宽表dataSets 里除了爬虫抓来的数据还有几份静态 CSV。countrydata.csv 是全球各国每日疫情数据china_provincedata.csv 是国内各省份疫情数据API_SP.POP.TOTL 开头的那份文件是国际人口统计数据作用是为后续计算感染率、病死率提供人口基数。多源数据要一起分析就必须做键值对齐和字段统一。我拆这份项目时看到数据集字段命名比较规整日期都是 YYYY-MM-DD 格式国家字段也做了统一。但实际分析中人口数据和疫情数据的国家名经常对不上比如疫情表里写“United States”人口表里写“United States of America”这时就需要手工映射或者用模糊匹配。一个合格的做法是先查看两边的唯一值列表然后写一个字典做名称归一化import pandas as pd country pd.read_csv(dataSets/countrydata.csv, encodingutf-8) pop pd.read_csv(dataSets/API_SP.POP.TOTL_DS2_zh_csv_v2_1075183.csv, encodingutf-8, skiprows4) name_map { 美国: United States, United States: United States, UK: United Kingdom, 英国: United Kingdom, } country[country_en] country[country].map(name_map).fillna(country[country]) merged country.merge(pop, left_oncountry_en, right_onCountry Code, howleft) merged[infection_rate] merged[confirmed] / merged[population] merged.to_csv(dataSets/merged_country_stats.csv, indexFalse, encodingutf-8)merge 时 howleft 表示以疫情数据为主表保留下每一条疫情记录人口数据匹配不到时就补 NaN。之后可以用 merged[population].fillna(merged[population].median()) 这类操作处理缺失值。skiprows4 是因为国际组织的人口统计表前几行通常是说明文字直接跳到表头所在行才能正确读入列名。这个清洗思路很典型先对齐键再处理缺失最后计算派生指标。3.3 社交平台评论的预处理从原始文本到干净语料社交平台评论这类文本数据比结构化 CSV 脏得多原始评论里包含大量 用户名、网页链接、表情符号和多余空白。如果直接把原始文本丢给 jieba 分词词频统计里会混入大量噪声情感分析的准确率也会被拉低。weiboProcess.py 干的就是这件事把爬虫抓回来的 raw CS 处理成干净语料处理后的数据再交给 NLP 链路。import pandas as pd import re df pd.read_csv(dataSets/weiboComments-5_21.csv, encodingutf-8) def clean_comment(text): text str(text) text re.sub(r[\w\u4e00-\u9fa5], , text) # 去掉 用户名 text re.sub(rhttp\S, , text) # 去掉网页链接 text re.sub(r\[.*?\], , text) # 去掉表情符号 text re.sub(r\s, , text).strip() # 合并冗余空白 return text df[clean_comment] df[comment].apply(clean_comment) df df[df[clean_comment] ! ] # 去掉清洗后为空的评论 df.to_csv(dataSets/weibo_cleaned.csv, indexFalse, encodingutf-8)清洗文本时正则表达式是最常用的工具上面四条替换规则基本覆盖了评论里最常见的噪声类型。需要注意 re.sub 的替换顺序先去掉 用户名和链接再去掉表情最后处理空白后两步不会误伤前面已经清理干净的文本。df[df[clean_comment] ! ] 这个布尔索引过滤掉了全部由噪声组成的评论这些空文本留在语料里只会让后续的分词结果出现大量空字符串影响 TF-IDF 特征矩阵的计算。清洗完的数据就可以直接喂给 NLP.ipynb 做下一步分析。4. 核心算法实现逻辑增长曲线与舆情情感分析的代码解读这套课设的核心算法集中在两个方向一是对疫情累计确诊数做时序预测对应 logistic.py 和那份算法设计文档二是对社交平台评论做情感分析和关键词挖掘对应 jiebafenci.py、tfidf.py、sentiments.py 和 weiboAnalyse.py。这两块也是课设报告里最容易拿分的内容下面直接拆代码讲实现。4.1 逻辑斯蒂增长模型logistic.py 如何拟合疫情曲线疫情累计确诊数的增长曲线在早期通常呈现出“缓慢增长 → 快速增长 → 增速放缓”的 S 型形态逻辑斯蒂增长函数是描述这类过程最经典的模型。logistic.py 做的事情就是用 scipy 的 curve_fit 对累计确诊数做非线性最小二乘拟合求解逻辑斯蒂函数中的三个关键参数。核心代码结构如下import numpy as np import pandas as pd from scipy.optimize import curve_fit # 逻辑斯蒂增长函数y L / (1 exp(-k * (t - t0))) def logistic(t, L, k, t0): return L / (1 np.exp(-k * (t - t0))) df pd.read_csv(dataSets/china_provincedata.csv, encodingutf-8) df[date] pd.to_datetime(df[date]) df df.sort_values(date) df[days] (df[date] - df[date].min()).dt.days x df[days].values.astype(float) y df[confirmed].values.astype(float) popt, _ curve_fit(logistic, x, y, p0[y.max(), 0.2, x[len(x) // 2]], maxfev10000) L, k, t0 popt df[pred] logistic(x, L, k, t0) df.to_csv(results/china_curve_pred.csv, indexFalse, encodingutf-8)这里的 logistic 函数有三个参数L 代表理论上限即最大累计确诊数k 代表感染增长速度t0 代表增速最快的拐点时间。p0 是 curve_fit 的迭代初值分别取“数据最大值、0.2、中间那天”这三个初值能让拟合过程更快收敛到合理区间。maxfev 是最大迭代次数非线性拟合偶尔不收敛调大这个值能减少“Optimal parameters not found”的报错概率。拟合结果写入 results/china_curve_pred.csv 后前端就能把真实曲线和预测曲线画在同一张图里直观展示模型效果。4.2 时序预测算法设计文档从实验设计到评估指标压缩包里的那份“新冠肺炎时序数据预测算法设计.docx”实际上就是课设报告的核心章节。我打开看的时候发现它并不是简单贴代码而是按标准实验报告结构写先交代数据来源和时间范围再阐述逻辑斯蒂模型相对线性回归的优势然后给出实验步骤和评价指标。对于打算照这个项目改自己课设的人来说这份文档的框架可以直接复用。时序预测实验中通常需要对预测结果做量化评估常见的指标是平均绝对误差 MAE 和均方根误差 RMSE。MAE 反映预测值与真实值的平均偏差RMSE 因为对大误差做了平方放大更能暴露预测的极端偏差。在报告里建议同时给出这两个指标再配合折线图直观展示拟合程度。文档里还对比了逻辑斯蒂模型与多项式回归的差异多项式回归容易过拟合且无法描述“增速放缓”的生物学含义而逻辑斯蒂模型参数有明确解释性这才是它被选中的核心理由。4.3 舆情情感分析分词、TF-IDF 与词云输出的完整链路NLP 部分的数据流是清洗后的评论 CSV 进入 jiebafenci.py 完成分词tfidf.py 将分词结果转成 TF-IDF 特征矩阵sentiments.py 基于情感词表对每一条评论打情感分最后 weiboWordData.py 统计高频词并生成词频 JSON 供前端 wordcloud.js 渲染词云。这条链路逻辑清晰每一步产出都落到 results 目录。import pandas as pd import jieba import re from sklearn.feature_extraction.text import TfidfVectorizer from collections import Counter df pd.read_csv(dataSets/weibo_cleaned.csv, encodingutf-8) def clean_text(s): s str(s) s re.sub(r[\w\u4e00-\u9fa5], , s) s re.sub(rhttp\S, , s) return s df[clean] df[clean_comment].apply(clean_text) df[cut] df[clean].apply(lambda x: .join(jieba.lcut(x))) # 去掉文本分类意义不大的高频词避免它们霸占特征空间 stop_words [疫情, 新冠, 肺炎, 一个, 什么, 可以] vec TfidfVectorizer(max_features2000, stop_wordsstop_words) X vec.fit_transform(df[cut]) # 统计高频词供前端词云展示 words [] for cut in df[cut]: words.extend(cut.split()) top100 Counter(words).most_common(100) with open(results/word_freq.json, w, encodingutf-8) as f: json.dump(dict(top100), f, ensure_asciiFalse)TfidfVectorizer 的两个参数值得展开说。max_features2000 限制特征维度为 2000避免评论语料量大时特征矩阵过于稀疏、训练时间过长stop_words 传入自定义停用词表把“疫情”“新冠”这类在语料里出现频率极高但对情感判断没有区分度的词去掉这样 TF-IDF 才会把权重分配给真正有意义的词。jieba.lcut 返回的是分词列表用空格 join 成字符串是 sklearn 文本向量化接口的常见输入格式。最终词频 JSON 用 ensure_asciiFalse 写出是为了保证中文关键词到了浏览器端还是中文否则词云上会显示一串 \uXXXX 编码。5. 运行避坑与常见问题虚拟环境、CDN 与中文乱码的修复实录这套课设能跑通但直接上手大概率会遇到几个经典问题。我把运行过程中最常翻车的五个点整理出来每个都按现象、原因、解决的顺序写方便对照排查。5.1 虚拟环境失效pyvenv.cfg 里写死了原机器路径现象解压后运行 python server.py报 ModuleNotFoundError: No module named flask但压缩包里明明带了虚拟环境目录。原因pyvenv.cfg 和虚拟环境目录里的脚本记录的是打包时那台机器的绝对路径换到新机器后 Python 解释器根本找不到原有的 site-packages相当于这个虚拟环境已经废了。解决不要依赖压缩包自带的虚拟环境删掉或者无视它自己重新创建。cd 到项目根目录后执行 python -m venv venv然后激活环境并用 pip install -r requirements.txt 安装依赖。如果项目里没有 requirements.txt就用 pip install flask pandas scipy scikit-learn jieba 逐个补齐。5.2 Flask 页面打开但图表空白ECharts 的 CDN 加载失败现象浏览器能显示页面框架但所有图表区域都是空白F12 控制台一堆红色报错提示 echarts.min.js 加载失败。原因templates 里的 HTML 通过 CDN 地址引用了 ECharts 的 JS 文件在能联网时没问题但网络不稳定、或者部署到内网环境时CDN 资源取不到图表自然渲染不出来。解决提前把 echarts.min.js、wordcloud.js 这些前端依赖下载到项目的 static 目录然后把 HTML 里的引用路径改成相对路径用 Flask 的 url_for 生成静态文件地址示例写法是 。改完之后整个项目在断网环境也能正常展示课设答辩现场最怕的就是网络卡顿导致大屏白屏。5.3 中文乱码CSV、JSON 和 HTML 三层都要盯现象地图上的省份名变成“口口”词云里全是方块字接口返回的 JSON 里则是一堆 \uXXXX。原因乱码有三个可能来源。CSV 文件本身可能是 GBK 编码但 pandas 默认用 utf-8 读Flask 的 jsonify 默认把中文转成 Unicode 转义序列HTML 页面没有声明 charset 导致浏览器用错误编码解析。解决读 CSV 时显式指定 encodingutf-8如果读出来是乱码就换成 encodinggbk 再试。Flask 端设置 app.config[JSON_AS_ASCII] False保证返回的中文原样输出。HTML 的 head 里加 。这三层都盯住中文显示问题基本能一次性解决。5.4 爬虫脚本抓不到数据页面改版之后选择器失效现象运行 weibo_战疫情爬虫_spider.py最后生成的 CSV 只有表头一条评论都没有。原因目标页面改版了脚本里写死的 CSS 选择器或者接口地址已经失效或者目标站点加了登录校验匿名请求拿不到数据。解决先别急着改代码用浏览器打开目标页面按 F12 查看当前真实的结构把新的选择器更新进去。接口失效的情况则优先找官方公开 API能用 API 就不要用页面解析。对于课设场景更稳妥的做法是先用现成的快照数据 CSV 把整体流程跑通爬虫作为补充演示模块不依赖实时数据也能完成汇报。5.5 Notebook 和脚本跑出的结果对不上执行顺序和数据入口不一致现象疫情分析和预测部分.ipynb 里画的预测曲线是平滑的但跑 scripts/logistic.py 生成的 results 里预测值明显不同。原因ipynb 是按单元格逐步执行的中间某些单元格手动改过数据DataFrame 停留在内存里就继续往下画了而脚本每次运行都重新读取原始 CSV两边数据入口不一致结果自然对不上。解决把数据入口统一成 dataSets 里的原始 CSVNotebook 里第一个单元格先重新读取文件之后所有分析都从这块 DataFrame 派生。跑之前用 Kernel 菜单的 Restart Run All 清空历史状态完整跑一遍不要中间手动跳过单元格。6. 二次开发思路把静态课设升级成可更新的分析平台课设交完只是第一步想把这个项目改造成能写进简历的作品可以从三个方向下手。第一个方向是把静态数据变成每日自动更新。dataSets 里的疫情 CSV 是一次性快照但疫情期间有很多公开数据接口每天更新用定时任务就能让项目自己滚动起来from apscheduler.schedulers.background import BackgroundScheduler def daily_update(): # 拉取最新数据覆盖写入 dataSets/countrydata.csv # 然后调用 scripts/update_analysis.py 重新生成预测和词频 print(数据已更新分析结果已重新生成) scheduler BackgroundScheduler() scheduler.add_job(daily_update, cron, hour9, minute0) scheduler.start()第二个方向是模型切换。logistic.py 里的 curve_fit 可以抽象成一个 fit_model(df) 接口预测函数统一返回包含 date、actual、pred 的 DataFrame然后新增 arima 或 prophet 模型实现同样的接口通过配置文件切换模型。这样报告里就能名正言顺地写“对比了三种时序模型”比单一模型更有说服力。第三个方向是给大屏加交互筛选。在 index.html 里加一个下拉框选择国家后前端 fetch 带参数请求后端接口后端按国家名过滤 CSV 返回子集前端用同一个折线图组件重新渲染即可。这个改动工程量不大但交互体验会明显提升答辩演示时也更有看点。拆完这份课设我有两点体会。第一完整链路比单一算法更重要哪怕每个模块都不深连起来就能撑起一篇像模像样的课设报告第二数据入口统一这件事永远是第一优先级从那以后我每次拿到新项目第一件事都是把入口数据格式固定下来再动手写分析否则后面返工全是血泪教训。希望这份拆解能帮到你尤其是那些正在为课程设计发愁、手头缺一个完整参考项目的同学。本文还有配套的精品资源点击获取