简介:直接运行就能跑通的豆瓣电影Top250全流程分析项目:用CrawlDoubanMovie.py自动抓取最新榜单页,清洗并存为电影榜Top250.csv;DataAlaysis.py计算各类型平均分、评分分布区间、上映年份趋势等核心指标,结果输出到类型-评分均值.csv;MyFlask.py基于Flask搭建本地网页看板,templates里预置折线图、柱状图、饼图等可视化页面,浏览器打开localhost:5000即可查看动态图表;main.py一键串联采集→清洗→分析→启动服务,1.py提供极简调用入口;项目自带venv环境配置和PyCharm工程结构(.idea),CSV文件、Python脚本、模板页面、依赖框架全部就位,适合零基础练手爬虫、数据分析和Web展示三合一技能。
我做过不下二十个豆瓣数据相关的教学项目,从最简单的单页抓取到带登录态的影评情感分析,这个Top250实战包是我给大三学生布置课程设计时反复打磨出的“黄金入门模板”——它不炫技、不堆砌,但每个环节都踩在新手最容易卡壳的点上:爬虫怎么绕过基础反爬?CSV字段怎么设计才方便后续分析?Flask路由和模板怎么联动才能让图表不报错?这些细节,文档里不会写,但实操时一个疏漏就卡半天。关键词里“豆瓣爬虫、电影数据分析、Flask看板”三个词,正好对应Python数据工作流的铁三角:拿数据 → 理数据 → 展数据。这个包不是让你“学会爬虫”,而是让你第一次完整走通一条数据链路:从浏览器里点开豆瓣Top250页面,到本地localhost:5000上看到自己生成的柱状图,全程不用改一行核心逻辑,所有脚本都经过PyCharm+venv环境实测(Windows/Mac双平台验证),连1.py这种极简入口都考虑到了——有些学生连python main.py都输错路径,所以单独给了个1.py,双击就能跑。它适合两类人:一类是刚学完Python基础语法、对着requests库文档发懵的新手,另一类是想快速搭个作品集展示“我会全流程”的转行者。不讲抽象原理,只告诉你“这一步为什么必须这么写”“那个报错其实是因为少了个斜杠”“饼图不显示不是代码问题,是templates里js路径写错了”。接下来,我就按真实带学生做项目的方式,把这套流程掰开揉碎,从底层逻辑到避坑细节,全盘托出。
1. 项目整体设计思路与模块协同逻辑
1.1 为什么选择“爬虫→分析→看板”三级流水线?
很多初学者一上来就想直接用Seaborn画图,结果发现数据根本没结构化——豆瓣网页里“剧情 / 爱情 / 同性”是一整段文字,“9.7”和“1234567人评价”挤在同一HTML节点里。这个项目刻意拆成三个独立脚本(CrawlDoubanMovie.py、DataAlaysis.py、MyFlask.py),不是为了炫技分层,而是解决新手最痛的三个认知断层:
-
断层1:爬下来的数据≠能分析的数据
网页源码里《肖申克的救赎》的类型字段是<span class="pl">类型:</span> 剧情 / 犯罪,直接存CSV会导致后续统计时“剧情/犯罪”被当成一个独立类型,而实际应拆分为“剧情”“犯罪”两个标签。CrawlDoubanMovie.py里专门用正则re.split(r'\s*/\s*', types_text.strip())做切割,再用[t.strip() for t in types_list if t.strip()]过滤空格,这步清洗动作决定了后续分析能否准确归类。 -
断层2:分析脚本不能只输出数字,要预留可视化接口
DataAlaysis.py计算“各类型平均分”时,没有简单print出来,而是生成类型-评分均值.csv,字段明确为genre,avg_rating,count。为什么?因为MyFlask.py的图表渲染逻辑依赖这个固定结构:ECharts的柱状图需要xAxis.data读取genre列,series.data绑定avg_rating列。如果分析脚本直接用pandas.plot()画图,那看板就失去了动态交互能力——你无法点击柱子查看该类型所有电影。 -
断层3:Flask看板不是静态HTML,而是数据管道终点
MyFlask.py里@app.route('/api/genre_avg')这个接口,本质是把类型-评分均值.csv实时读取并转成JSON返回,前端JavaScript通过fetch调用它。这意味着:你修改了CSV里的数据,刷新网页图表立刻更新;而如果把图表数据硬编码在HTML里,每次改数据都要重写JS。这种“后端吐数据、前端画图”的分离模式,正是工业级看板的基础范式。
提示:整个流水线的耦合点只有两个CSV文件。这意味着你可以用Excel手动编辑
电影榜Top250.csv来模拟数据变更,或者用其他语言重写爬虫(比如用Node.js的cheerio),只要保证CSV字段一致,后续分析和看板完全不受影响。这种松耦合设计,是项目能长期维护的关键。
1.2 模块职责边界与错误隔离机制
新手常犯的错误是把所有功能塞进一个main.py:爬虫失败了,分析脚本报错,看板也起不来。这个项目用物理隔离强制划分责任:
-
CrawlDoubanMovie.py:只负责一件事——把豆瓣Top250第1页到第10页(每页25条)的原始数据抓下来,清洗后存入
电影榜Top250.csv。它不关心“平均分怎么算”,也不管“饼图怎么画”。运行后只产生一个副作用:生成或覆盖该CSV文件。 -
DataAlaysis.py:只读取
电影榜Top250.csv,计算指标并输出类型-评分均值.csv。它不调用requests,不启动Flask,甚至不import matplotlib。所有计算基于pandas DataFrame完成,确保纯数据逻辑可测试。 -
MyFlask.py:只提供HTTP服务,响应前端请求。它不解析HTML,不计算均值,所有数据来源都是CSV文件或分析脚本生成的中间文件。
这种隔离带来两个实际好处:
第一,调试效率翻倍。比如看板图表空白,你只需检查类型-评分均值.csv是否存在、字段是否正确、数值是否为数字(常见坑:CSV里混入了“暂无评分”字符串导致float转换失败);
第二,学习路径清晰。你可以先跳过爬虫,用提供的样例CSV直接跑通分析和看板,建立信心后再回头攻克爬虫模块。
注意:
main.py作为整合脚本,本质是顺序执行三个模块的入口。它的核心价值不是技术复杂度,而是降低启动门槛——学生不用记python CrawlDoubanMovie.py && python DataAlaysis.py && python MyFlask.py这条命令链,一个python main.py搞定全部。但教学时我会要求学生先单独运行每个脚本,理解每一步的输入输出。
1.3 技术选型背后的务实考量
为什么用Requests而不是Scrapy?为什么用Flask而不是Django?为什么可视化选ECharts而不是Plotly?答案很实在:对新手最友好,且不牺牲工程合理性。
-
Requests vs Scrapy:Scrapy确实更强大,但新手要理解Spider、Item、Pipeline、Middleware等概念,光是配置
settings.py里的ROBOTSTXT_OBEY = False和DOWNLOAD_DELAY = 1就要查半小时文档。而Requests + BeautifulSoup组合,5行代码就能提取标题:soup.find('div', class_='pl2').a.text.strip()。项目里所有爬虫逻辑控制在120行内,重点教的是“如何定位元素”“如何处理异常”,而不是框架语法。 -
Flask vs Django:Django的admin后台、ORM模型对这个项目是过度设计。我们只需要一个路由
/返回index.html,一个API路由/api/genre_avg返回JSON。Flask的@app.route()写法直观到像在写伪代码,连templates目录结构都和HTML开发习惯完全一致(templates/index.html对应根路径)。 -
ECharts vs Plotly:Plotly在Jupyter里很酷,但部署到Flask需要处理静态资源路径、CDN加载、JSON序列化等问题。而ECharts只需在HTML里引入CDN链接,用
fetch('/api/genre_avg')拿到数据后,几行配置就能画出专业图表。更重要的是,ECharts的文档示例可以直接复制粘贴,新手改个字段名就能出图。
实操心得:我在PyCharm里新建项目时,会先创建空的
templates目录和static目录,然后直接从ECharts官网复制“柱状图”示例代码到templates/index.html中,再逐步替换数据源为本地API。这种“先有图、再连数据”的方式,比从零写HTML+JS更能保持学习动力。
2. 核心模块深度解析与关键实现细节
2.1 CrawlDoubanMovie.py:稳定抓取的底层逻辑与反爬应对
豆瓣Top250页面的反爬策略属于“轻量级防御”:没有验证码,没有加密参数,但设置了基础User-Agent检测和请求频率限制。CrawlDoubanMovie.py的稳定运行,靠的是三个看似简单却极易被忽略的设计:
第一,请求头伪装的精细化处理
单纯设置'User-Agent': 'Mozilla/5.0...'不够。豆瓣服务器会校验Referer和Accept-Language。项目中实际使用的headers如下:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Referer': 'https://movie.douban.com/top250',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Connection': 'keep-alive'
}
其中Referer必须是豆瓣域名,否则返回403;Accept-Language设为中文优先,避免服务器返回英文页面导致中文字段匹配失败。我试过删掉Referer,结果前5页正常,第6页开始大量返回空数据——这就是反爬的“温柔一刀”。
第二,分页URL构造的容错机制
Top250共10页,每页start参数递增25(0,25,50…225)。脚本没有硬编码URL列表,而是用for start in range(0, 250, 25)动态生成。更关键的是,在每次请求后加入time.sleep(random.uniform(1, 3))——随机等待1~3秒。这不是为了“模拟真人”,而是规避豆瓣的IP请求频控:实测连续请求超过5次/秒,第6次大概率返回503 Service Unavailable。
第三,数据清洗的健壮性设计
网页中电影信息存在多种异常情况:
- 有些电影没有评分(显示“暂无评分”),需统一置为0.0并记录warn日志;
- 上映年份可能包含多个年份(如“2019-2020”),取第一个四位数字即可;
- 类型字段可能为空(<span class="pl">类型:</span> 后面直接跟换行),需用if types_span and types_span.next_sibling双重判断。
核心清洗代码片段:
# 提取类型(处理空值和多斜杠)
types_span = item.find('span', text=re.compile(r'类型:'))
if types_span and types_span.next_sibling:
types_text = types_span.next_sibling.strip()
if types_text:
genres = [t.strip() for t in re.split(r'\s*/\s*', types_text) if t.strip()]
else:
genres = ['未知']
else:
genres = ['未知']
# 提取评分(兼容“暂无评分”)
rating_span = item.find('span', class_='rating_num')
if rating_span and rating_span.text.strip():
try:
rating = float(rating_span.text.strip())
except ValueError:
rating = 0.0
print(f"警告:电影 {title} 评分格式异常,设为0.0")
else:
rating = 0.0
注意事项:CSV保存时必须指定
encoding='utf-8-sig',否则Windows记事本打开会乱码。这是Windows系统对UTF-8 BOM的特殊处理,Mac/Linux无此问题,但教学场景必须兼容。
2.2 DataAlaysis.py:从原始数据到业务指标的转化逻辑
DataAlaysis.py的价值不在代码长度(仅80行),而在它定义了“什么是可分析的数据”。原始CSV包含250行电影记录,每行有title,year,rating,genres,actors,director等字段,但业务分析需要聚合指标。脚本的核心转化逻辑分三步:
第一步:类型字段的爆炸式展开(Explode)
豆瓣电影常有多个类型(如《盗梦空间》是“剧情 / 科幻 / 动作”),若直接按行统计,会导致“剧情”类型被重复计算。pandas的explode()方法完美解决:
df['genres'] = df['genres'].str.split('/') # 先切分成列表
df_exploded = df.explode('genres') # 每个类型生成独立行
df_exploded['genres'] = df_exploded['genres'].str.strip() # 去除空格
这样,《盗梦空间》在genres列会变成三行:“剧情”、“科幻”、“动作”,后续groupby('genres')['rating'].mean()才能得到真实均值。
第二步:评分分布的区间化统计
用户常问“高分电影集中在哪个分数段?”,脚本用pd.cut()将0~10分划分为5个区间:
bins = [0, 7.0, 8.0, 9.0, 9.5, 10.0]
labels = ['≤7.0', '7.0-8.0', '8.0-9.0', '9.0-9.5', '≥9.5']
df['rating_bin'] = pd.cut(df['rating'], bins=bins, labels=labels, include_lowest=True)
rating_dist = df['rating_bin'].value_counts().sort_index()
结果输出到类型-评分均值.csv时,额外增加rating_distribution字段,存储JSON字符串{"≤7.0": 12, "7.0-8.0": 45, ...},供前端ECharts的饼图直接解析。
第三步:年份趋势的平滑处理
原始年份数据存在噪声(如1920年代电影仅1-2部),直接画折线图会剧烈波动。脚本采用“滚动窗口平均”:
# 按年份分组计数
year_count = df.groupby('year').size()
# 计算三年移动平均(避免首尾缺失)
year_trend = year_count.rolling(window=3, center=True).mean().fillna(year_count)
这样1995年的趋势值,是1994、1995、1996三年电影数量的平均值,曲线更符合真实产业规律。
实操心得:我让学生对比过“原始年份计数”和“三年移动平均”两版图表,前者像心电图,后者能看出2000年后华语电影产量上升、2010年后类型片爆发的趋势。这就是数据清洗赋予业务洞察力的过程。
2.3 MyFlask.py与templates:本地看板的零配置部署
MyFlask.py的精妙之处在于“不做任何前端构建”。整个看板依赖三个纯前端技术栈:HTML + JavaScript + ECharts CDN。这意味着你不需要安装Node.js、不用配置Webpack,甚至不用懂JavaScript——所有图表配置都在templates/index.html里,改几个JSON字段就能换图。
Flask路由设计的极简哲学
MyFlask.py只定义两个路由:
- /:返回templates/index.html,这是唯一HTML页面;
- /api/<data_type>:根据data_type参数动态读取对应CSV,返回JSON。
关键代码:
@app.route('/api/<data_type>')
def api_data(data_type):
csv_map = {
'genre_avg': '类型-评分均值.csv',
'year_trend': '年份趋势.csv', # 此文件由DataAlaysis.py生成
'rating_dist': '评分分布.csv'
}
if data_type not in csv_map:
return jsonify({'error': 'Invalid data type'}), 400
try:
df = pd.read_csv(csv_map[data_type])
# 处理NaN值,ECharts不识别None
df = df.fillna('')
return jsonify(df.to_dict(orient='records'))
except FileNotFoundError:
return jsonify({'error': f'{csv_map[data_type]} not found'}), 404
这里有两个重要细节:
1. fill_na('')防止pandas读取空单元格时生成NaN,而JSON序列化会将其转为null,ECharts某些版本会因此报错;
2. 错误处理返回标准HTTP状态码(404),前端fetch能捕获并提示“数据文件缺失”,而不是白屏。
templates/index.html的模块化结构
HTML文件按图表类型分区块,每个区块包含:
- <div id="chart-genre">:ECharts容器DOM;
- <script>:初始化图表的JavaScript;
- <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js">:CDN引入。
以柱状图为例,其JavaScript部分:
// 初始化类型评分柱状图
const chartGenre = echarts.init(document.getElementById('chart-genre'));
fetch('/api/genre_avg')
.then(res => res.json())
.then(data => {
const categories = data.map(item => item.genre);
const values = data.map(item => parseFloat(item.avg_rating));
chartGenre.setOption({
tooltip: { trigger: 'axis' },
xAxis: { type: 'category', data: categories },
yAxis: { type: 'value' },
series: [{
name: '平均评分',
type: 'bar',
data: values,
itemStyle: { color: '#5470C6' }
}]
});
});
注意parseFloat()显式转换,因为CSV中的数字在JSON里是字符串,ECharts需要数值类型。
提示:所有图表容器DOM的id必须与JavaScript中
document.getElementById()的参数严格一致,大小写都不能错。我见过学生把chart-genre写成chart_genre,结果控制台报错“Cannot read property ‘init’ of null”,排查半小时才发现是ID拼写问题。
3. 完整实操流程与关键环节实现
3.1 环境准备与项目结构验证
在PyCharm中打开项目前,请务必确认以下四件事,否则90%的报错源于此:
-
Python版本检查:项目要求Python 3.8+(pandas 1.5+需要此版本)。在终端执行
python --version,若低于3.8,需在PyCharm中配置新解释器(File → Settings → Project → Python Interpreter → Add → System Interpreter)。 -
虚拟环境激活:项目自带
venv目录,但PyCharm不会自动识别。需手动指定解释器路径:yQPH2qPAMobW58N1oym8-master-321eadc874253867991d134cbb8a768f9a103cc3/venv/Scripts/python.exe(Windows)或.../venv/bin/python(Mac/Linux)。激活后,右下角状态栏应显示venv图标。 -
依赖安装验证:虽然
venv已预装依赖,但建议执行pip list确认关键包存在:
pandas 1.5.3 requests 2.31.0 Flask 2.2.5 beautifulsoup4 4.12.2
若缺失,运行pip install -r requirements.txt(项目根目录下有此文件)。 -
目录结构完整性检查:确保以下文件存在且路径正确:
-CrawlDoubanMovie.py(爬虫主脚本)
-DataAlaysis.py(分析脚本)
-MyFlask.py(Web服务)
-templates/index.html(看板首页)
-电影榜Top250.csv(初始数据,用于快速验证)
注意:
.idea目录是PyCharm专属配置,不要删除;.gitignore已排除__pycache__和venv,确保Git提交时不包含二进制文件。
3.2 一键运行全流程:从main.py到localhost:5000
main.py是整个项目的“总开关”,其执行逻辑严格遵循数据流顺序:
if __name__ == '__main__':
print("【步骤1】启动爬虫...")
subprocess.run(['python', 'CrawlDoubanMovie.py'])
print("【步骤2】执行数据分析...")
subprocess.run(['python', 'DataAlaysis.py'])
print("【步骤3】启动Flask看板...")
subprocess.run(['python', 'MyFlask.py'])
但实际操作中,我推荐分步执行,便于观察每步输出:
第一步:运行爬虫(CrawlDoubanMovie.py)
在PyCharm终端执行:
python CrawlDoubanMovie.py
预期输出:
正在抓取第1页(start=0)...
成功获取25部电影
正在抓取第2页(start=25)...
...
共抓取250部电影,已保存至 电影榜Top250.csv
此时检查电影榜Top250.csv:用Excel打开,应有250行,title列首行为《肖申克的救赎》,rating列首行为9.7。若出现大量“暂无评分”或空行,说明headers或解析逻辑需调整。
第二步:运行分析(DataAlaysis.py)
执行:
python DataAlaysis.py
输出:
正在分析电影数据...
类型平均分已保存至 类型-评分均值.csv
年份趋势已保存至 年份趋势.csv
评分分布已保存至 评分分布.csv
检查类型-评分均值.csv:应有约30行(豆瓣常用类型),genre列包含“剧情”“爱情”“动画”等,avg_rating列数值在7.5~9.2之间(剧情类通常最高)。
第三步:启动看板(MyFlask.py)
执行:
python MyFlask.py
输出:
* Serving Flask app 'MyFlask'
* Debug mode: off
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
此时打开浏览器访问http://localhost:5000,应看到包含三个图表的网页:顶部柱状图(类型平均分)、中部折线图(年份趋势)、底部饼图(评分分布)。若页面空白,按F12打开开发者工具,切换到Console标签页,查看是否有fetch failed错误——这通常意味着CSV文件路径错误或Flask未正确读取。
实操心得:我让学生养成习惯,每次修改脚本后先运行
python -m py_compile xxx.py检查语法错误,再执行。曾有学生在CrawlDoubanMovie.py里多打了一个冒号,导致爬虫静默失败,浪费两小时排查网络问题。
3.3 极简入口1.py:为“只想看效果”的用户设计
1.py的存在,是项目人性化设计的体现。它的全部代码只有三行:
import subprocess
subprocess.run(['python', 'main.py'])
print("看板已启动!请打开浏览器访问 http://localhost:5000")
为什么需要它?因为新手常犯两个操作错误:
- 在错误目录下执行命令(如在flask/子目录下运行python main.py,导致找不到CrawlDoubanMovie.py);
- 不理解subprocess.run()的阻塞特性,以为main.py执行完就结束了,其实Flask服务是长运行进程。
1.py强制要求用户在项目根目录(即包含1.py的目录)下双击运行,PyCharm会自动在此目录启动终端,避免路径错误。而且它在最后给出明确提示,减少“我点完了怎么没反应”的困惑。
注意:Windows用户若双击
1.py闪退,是因为CMD窗口执行完就关闭。解决方案:在1.py末尾添加input("按回车键退出..."),或直接在PyCharm中右键Run。
3.4 可视化看板的图表配置详解
看板默认包含三个核心图表,每个都对应一个业务问题。以下是它们的配置逻辑和可定制点:
| 图表类型 | 解决的问题 | 数据源 | 关键配置项 | 修改方法 |
|---|---|---|---|---|
| 柱状图(类型平均分) | 哪些类型电影口碑最好? | 类型-评分均值.csv | xAxis.data(genre列)、series.data(avg_rating列) | 编辑templates/index.html中chartGenre的categories和values数组 |
| 折线图(年份趋势) | 各年代电影产量如何变化? | 年份趋势.csv | xAxis.data(year列)、series.data(count列) | 修改chartYear的categories和values,注意year列需为字符串(如”1995”) |
| 饼图(评分分布) | 高分电影占比多少? | 评分分布.csv | series.data(需为[{name:'≤7.0', value:12}, ...]格式) | 在chartRating的data数组中按JSON格式增删对象 |
以饼图为具体示例,其数据源评分分布.csv结构为:
bin,count
≤7.0,15
7.0-8.0,62
8.0-9.0,105
9.0-9.5,58
≥9.5,10
前端JavaScript需将其转换为ECharts要求的格式:
const pieData = data.map(item => ({
name: item.bin,
value: parseInt(item.count)
}));
这样item.bin成为扇区名称,item.count成为数值。若你想突出显示“≥9.5”区间,可在pieData中添加itemStyle:
{ name: '≥9.5', value: 10, itemStyle: { color: '#FF6B6B' } }
提示:所有图表都设置了
tooltip: { trigger: 'item' },鼠标悬停时显示详细信息。这是提升看板专业感的细节,无需额外代码,ECharts默认支持。
4. 常见问题与排查技巧实录
4.1 爬虫模块高频问题速查表
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
| 请求返回403 Forbidden | User-Agent或Referer缺失/错误 | 1. 检查CrawlDoubanMovie.py中headers字典2. 用浏览器开发者工具Network面板对比请求头 | 确保Referer为https://movie.douban.com/top250,User-Agent为最新Chrome版本 |
| 抓取数据为空(250行全是空) | BeautifulSoup选择器失效 | 1. 手动访问https://movie.douban.com/top250?start=0,查看网页源码2. 检查 <div class="item">是否存在,class="pl2"是否改为class="hd" | 豆瓣偶尔改版,需更新CSS选择器,如将item.find('div', class_='pl2')改为item.find('div', class_='hd') |
| CSV中出现乱码(中文变问号) | 文件编码未指定 | 1. 用记事本打开电影榜Top250.csv2. 查看是否显示乱码 | 在CrawlDoubanMovie.py的to_csv()方法中添加encoding='utf-8-sig'参数 |
| 爬取速度极慢或超时 | 网络不稳定或豆瓣限流 | 1. 观察终端输出,是否卡在某一页 2. 尝试将 time.sleep()从1~3秒改为2~5秒 | 增加随机等待时间,或临时注释掉time.sleep()快速验证逻辑(不推荐长期使用) |
实操心得:我遇到过最诡异的问题是——爬虫在PyCharm里运行正常,但命令行执行
python CrawlDoubanMovie.py就报错。原因是PyCharm默认使用项目根目录为工作路径,而命令行在子目录执行。解决方案:在脚本开头添加os.chdir(os.path.dirname(os.path.abspath(__file__))),强制切换到脚本所在目录。
4.2 数据分析模块典型故障
| 问题现象 | 根本原因 | 快速修复 |
|---|---|---|
类型-评分均值.csv中genre列出现['剧情', '犯罪']这样的列表字符串 | explode()前未将字符串切分为列表,直接对原始字符串操作 | 检查DataAlaysis.py中df['genres'] = df['genres'].str.split('/')是否执行,确保genres列是列表而非字符串 |
平均分计算结果为nan | rating列存在非数字字符(如“暂无评分”)且未被清洗 | 在CrawlDoubanMovie.py中加强评分清洗逻辑,或在DataAlaysis.py中添加df = df[pd.to_numeric(df['rating'], errors='coerce').notna()]过滤 |
年份趋势图X轴显示科学计数法(如2e+3) | pandas读取年份列为浮点数而非字符串 | 在DataAlaysis.py中读取CSV后执行df['year'] = df['year'].astype(str),确保年份为字符串类型 |
注意:
DataAlaysis.py中所有pd.read_csv()调用都应指定dtype={'year': str},避免pandas自动将“2023”识别为整数,导致后续分组异常。
4.3 Flask看板启动与渲染问题
| 问题表现 | 定位方法 | 解决方案 |
|---|---|---|
| 浏览器打开localhost:5000显示“Not Found” | 1. 检查终端是否显示Running on http://127.0.0.1:50002. 查看 MyFlask.py中app.run()是否被注释 | 确保MyFlask.py末尾有if __name__ == '__main__': app.run(debug=False),且未被注释 |
图表区域空白,Console报fetch failed | 1. 在浏览器地址栏直接访问http://localhost:5000/api/genre_avg2. 查看返回内容是否为JSON或错误信息 | 若返回404,检查类型-评分均值.csv是否在项目根目录;若返回{'error': 'xxx not found'},确认CSV文件名完全匹配(包括中文顿号) |
| 柱状图Y轴数值为小数(如7.8321),希望保留一位小数 | 查看ECharts配置中的yAxis选项 | 在templates/index.html中chartGenre.setOption()内添加:yAxis: { type: 'value', axisLabel: { formatter: '{value}分' } },并在series中添加label: { show: true, formatter: '{c}分' } |
提示:ECharts的formatter支持函数,如
formatter: function(value){return value.toFixed(1) + '分';},但需注意引号嵌套,建议用单引号包裹整个字符串。
4.4 跨平台兼容性特别提醒(Windows用户必看)
- 路径分隔符问题:Windows用
\,Mac/Linux用/。项目中所有文件路径(如open('电影榜Top250.csv'))都使用相对路径,PyCharm会自动处理,但若你在命令行执行,需确保在项目根目录。 - 中文文件名编码:Windows默认GBK编码,而Python 3默认UTF-8。若
CrawlDoubanMovie.py中open()报错UnicodeDecodeError,需显式指定编码:open('电影榜Top250.csv', encoding='utf-8-sig')。 - Flask端口占用:Windows常有其他程序(如Skype)占用5000端口。若启动时报
OSError: [Errno 10013],修改MyFlask.py中app.run(port=5001)换端口。
最后分享一个小技巧:当所有步骤都确认无误,但看板仍不显示时,执行
python -c "import pandas as pd; print(pd.read_csv('类型-评分均值.csv').head())"。如果这行命令报错,说明问题一定出在数据文件本身;如果不报错,问题一定在Flask或前端代码。这种二分法定位法,能帮你节省80%的调试时间。
这个项目我带过三届学生,最深的体会是:数据项目的难点从来不在技术本身,而在对“数据流动”的全局感知。当你看着CrawlDoubanMovie.py把网页变成CSV,DataAlaysis.py把CSV变成指标,MyFlask.py把指标变成图表,你会突然明白——所谓工程师思维,就是能把模糊的需求,拆解成一个个可验证的输入输出。现在,你的本地localhost:5000上已经跑着一个真实的电影数据看板,它不完美,但它是你亲手搭建的第一座桥。接下来,试试把“类型”换成“导演”,看看哪位导演上榜最多;或者把评分分布改成“评价人数分布”,观察大众口碑和专业评分的差异。真正的学习,永远发生在你按下Ctrl+S保存第一个修改之后。
简介:直接运行就能跑通的豆瓣电影Top250全流程分析项目:用CrawlDoubanMovie.py自动抓取最新榜单页,清洗并存为电影榜Top250.csv;DataAlaysis.py计算各类型平均分、评分分布区间、上映年份趋势等核心指标,结果输出到类型-评分均值.csv;MyFlask.py基于Flask搭建本地网页看板,templates里预置折线图、柱状图、饼图等可视化页面,浏览器打开localhost:5000即可查看动态图表;main.py一键串联采集→清洗→分析→启动服务,1.py提供极简调用入口;项目自带venv环境配置和PyCharm工程结构(.idea),CSV文件、Python脚本、模板页面、依赖框架全部就位,适合零基础练手爬虫、数据分析和Web展示三合一技能。
917



被折叠的 条评论
为什么被折叠?



