猫眼电影Top100数据链路实战:Python爬取、清洗、分析与可视化大屏

发布时间:2026/9/23 2:10:52
猫眼电影Top100数据链路实战:Python爬取、清洗、分析与可视化大屏 简介这是一套面向高校学生与Python初学者的猫眼电影数据爬取、分析与可视化课程设计项目可直接用于期末大作业或课程设计参考。项目以Python为核心完整实现了数据采集、清洗分析到图表展示的全流程代码注释详尽零基础也能读懂同时预留了二次开发空间。压缩包共42个文件约422KB包含11个py源码文件、9个pyc编译文件、5个xml配置、5个html页面、5个js脚本以及sql、json、ini、readme等辅助文件覆盖爬虫脚本、数据模型、模板页面与项目配置等模块下载后即可运行。目前已有837人学习下载说明其参考价值得到一定认可。项目附带详细文档说明曾获97分高分评价读者可据此掌握爬取策略、数据存储与可视化呈现的完整思路并借鉴目录组织与代码结构快速完成自己的课程设计或在此基础上扩展新功能。1. 猫眼电影数据链路从页面请求到可视化大屏一套能交差的完整方案期末大作业和课程设计最怕什么不是不会写代码是数据拿不到、图太丑、答辩时说不清每一步在干什么。猫眼电影这个选题每年都有人做但大多数人卡在三个地方请求头没带对导致返回空数据、字段命名混乱导致后面分析没法做、可视化只会画柱状图。这套方案用 Python 把爬取、清洗、分析、可视化串成一条完整链路最终产出一个能放答辩 PPT 的交互式大屏。适合正在赶课程设计 deadline 的本科生也适合想补一个完整数据分析项目经验的自学者。下面从环境配置开始每一步都给出可复现的命令和代码。2. 环境搭建与目标页面结构拆解先看清数据藏在哪2.1 Python 环境与依赖库的版本选择很多人第一步就翻车——用 Python 3.12 装某些老版本库会报编译错误。我一般建议用 Python 3.10 或 3.11兼容性最稳。如果你还没装 Python去官网下载安装包时记得勾选“Add Python to PATH”不然后面在终端敲python会提示找不到命令。装完用下面两行确认python --version pip --version接下来装依赖。不要一个个pip install直接写一个requirements.txtrequests2.31.0 pandas2.1.4 numpy1.26.2 pyecharts2.0.4 jieba0.42.1 wordcloud1.9.3 lxml5.1.0然后执行pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这里指定了清华镜像源下载速度会快很多。requests负责发请求pandas做数据清洗和分析pyecharts生成交互式图表jieba和wordcloud用于生成词云。版本号是我在多个项目里验证过能互相兼容的组合不建议随意升级大版本。提示如果你用 VS Code装一个 Python 扩展然后在左下角选对解释器路径否则终端里 pip 装上了、编辑器里 import 还是报红。2.2 猫眼榜单页面的 DOM 结构与请求规律打开猫眼电影榜单页按 F12 切到 Network 面板刷新页面找到第一个 HTML 文档请求。你会看到返回的是一整段 HTML数据直接嵌在dd标签里而不是通过 XHR 异步加载的 JSON。这意味着不需要分析接口签名直接用requests拿页面再解析就行。但有几个关键点必须注意。第一请求头里必须带User-Agent否则猫眼会返回一个验证页面而不是真实数据。第二Cookie里有一个uuid字段虽然短期不携带也能拿到数据但频繁请求后会被限流建议首次访问后把 Cookie 保存下来复用。第三榜单分页通过 URL 参数offset控制每页 10 条翻页时 offset 递增 10。用下面这段代码验证页面能否正常获取import requests 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://www.maoyan.com/, } url https://www.maoyan.com/board/4?offset0 resp requests.get(url, headersheaders, timeout10) print(resp.status_code) print(len(resp.text))如果status_code返回 200 且len(resp.text)大于 50000说明拿到了完整页面。如果返回内容很短大概率是被识别成了爬虫需要检查 User-Agent 是否完整、是否带了 Referer。2.3 用 XPath 定位排名、片名、主演和评分字段拿到 HTML 后用lxml解析。猫眼榜单的每个条目结构大致是外层dd包裹里面i classboard-index是排名p classname里a的文本是片名p classstar是主演p classscore里i classinteger和i classfraction拼起来是评分。from lxml import etree html etree.HTML(resp.text) items html.xpath(//dd) for item in items[:3]: rank item.xpath(.//i[classboard-index]/text())[0] name item.xpath(.//p[classname]/a/text())[0] star item.xpath(.//p[classstar]/text())[0].strip() score_int item.xpath(.//p[classscore]/i[classinteger]/text())[0] score_frac item.xpath(.//p[classscore]/i[classfraction]/text())[0] print(rank, name, star, score_int score_frac)XPath 里的.表示从当前节点往下找//表示任意层级。text()取文本内容。注意star字段取出来会带换行和空格必须.strip()处理。评分是整数部分和小数部分分开存的拼接时直接字符串相加即可后面再转 float。3. 爬取脚本的健壮性设计翻页、限速与断点续爬3.1 翻页逻辑与请求间隔控制榜单一共 10 页每页 10 条总共 100 条数据。翻页就是改 offset第一页 0第二页 10以此类推。但不要用for i in range(10)一口气发完猫眼对高频请求有风控。我一般每请求一页后time.sleep(1.5)模拟人工浏览节奏。import time import random all_movies [] for page in range(10): offset page * 10 url fhttps://www.maoyan.com/board/4?offset{offset} resp requests.get(url, headersheaders, timeout10) if resp.status_code ! 200: print(f第{page1}页请求失败状态码{resp.status_code}) continue html etree.HTML(resp.text) items html.xpath(//dd) if not items: print(f第{page1}页未解析到数据可能被限流) break for item in items: try: rank item.xpath(.//i[classboard-index]/text())[0] name item.xpath(.//p[classname]/a/text())[0] star item.xpath(.//p[classstar]/text())[0].strip() score_int item.xpath(.//p[classscore]/i[classinteger]/text())[0] score_frac item.xpath(.//p[classscore]/i[classfraction]/text())[0] all_movies.append({ rank: int(rank), name: name, star: star, score: float(score_int score_frac) }) except IndexError: continue time.sleep(1.5 random.random())random.random()加一点随机抖动避免固定间隔被识别出规律。try...except IndexError是防止某一条数据字段缺失导致整个循环崩掉。如果某一页返回空列表直接break退出不要继续请求后面几页否则可能触发更严格的限制。3.2 把爬取结果落盘成 CSV 与 SQLite 双备份数据拿到后不要只放内存里程序一崩就全没了。我习惯同时存 CSV 和 SQLiteCSV 方便用 Excel 打开检查SQLite 方便后面用 SQL 做聚合分析。import pandas as pd import sqlite3 df pd.DataFrame(all_movies) df.to_csv(maoyan_top100.csv, indexFalse, encodingutf-8-sig) conn sqlite3.connect(maoyan.db) df.to_sql(top100, conn, if_existsreplace, indexFalse) conn.close() print(f共写入 {len(df)} 条记录)encodingutf-8-sig是为了让 Excel 打开 CSV 时不乱码。if_existsreplace表示每次运行都覆盖旧表避免重复数据堆积。如果你需要断点续爬可以在循环里每爬完一页就追加写入一次而不是等全部爬完再写。3.3 被限流后的排查与恢复策略如果发现返回的 HTML 里没有dd标签或者状态码变成 403说明被限流了。先检查三件事User-Agent 是否完整、请求间隔是否太短、Cookie 是否过期。恢复方法是换一个 User-Agent把time.sleep调到 3 秒以上然后重新从第一页开始。如果还是不行等 10 分钟再试。注意不要用多线程或异步去加速爬取猫眼的数据量很小100 条记录单线程完全够用加速反而容易触发风控。4. 数据清洗与分析把原始字段变成能回答问题的指标4.1 主演字段的拆分与类型转换爬下来的star字段格式是“主演张三,李四,王五”需要拆成列表。评分已经是 float排名是 int这两列不用动。但star里可能包含“主演”前缀也可能有的条目没有主演信息。df pd.read_csv(maoyan_top100.csv) def parse_stars(text): if pd.isna(text): return [] text text.replace(主演, ).strip() return [s.strip() for s in text.split(,) if s.strip()] df[star_list] df[star].apply(parse_stars) df[star_count] df[star_list].apply(len) print(df[[name, star_count]].head())parse_stars先去掉前缀再按逗号切分最后过滤空字符串。star_count这一列后面可以用来分析“主演人数是否影响评分”。4.2 评分分布与排名区间的交叉分析用 pandas 的cut把评分分成几个区间然后看每个区间里有多少部电影。bins [0, 8.0, 8.5, 9.0, 9.5, 10.0] labels [8.0以下, 8.0-8.5, 8.5-9.0, 9.0-9.5, 9.5以上] df[score_range] pd.cut(df[score], binsbins, labelslabels, rightFalse) score_dist df[score_range].value_counts().sort_index() print(score_dist)rightFalse表示左闭右开区间比如 8.0 会落在“8.0-8.5”而不是“8.0以下”。这个分布能直接回答“猫眼 Top100 的评分集中在哪个段位”。4.3 用 jieba 对片名做词频统计片名里往往包含高频词比如“之”“的”“传”等。用 jieba 分词后统计词频可以做成词云。import jieba from collections import Counter all_words [] for name in df[name]: words jieba.lcut(name) all_words.extend([w for w in words if len(w) 1]) word_freq Counter(all_words).most_common(20) for word, count in word_freq: print(word, count)len(w) 1过滤掉单字因为单字词云里没有意义。most_common(20)取前 20 个高频词后面直接喂给 pyecharts 的词云组件。5. 可视化落地用 pyecharts 生成可交互的 HTML 大屏5.1 评分分布柱状图与排名折线图pyecharts 生成的图表是 HTML 文件可以直接用浏览器打开也可以嵌入答辩 PPT 里截图。先画评分分布柱状图from pyecharts.charts import Bar from pyecharts import options as opts bar ( Bar() .add_xaxis(score_dist.index.tolist()) .add_yaxis(电影数量, score_dist.values.tolist()) .set_global_opts( title_optsopts.TitleOpts(title猫眼Top100评分分布), xaxis_optsopts.AxisOpts(name评分区间), yaxis_optsopts.AxisOpts(name数量), ) ) bar.render(score_dist.html)add_xaxis传类别add_yaxis传数值。set_global_opts里设置标题和坐标轴名称。生成的score_dist.html用浏览器打开就能看到交互效果鼠标悬停会显示具体数值。5.2 主演词云与片名词云的双图联动词云用WordCloud组件from pyecharts.charts import WordCloud wc ( WordCloud() .add(, word_freq, word_size_range[12, 60]) .set_global_opts(title_optsopts.TitleOpts(title片名高频词云)) ) wc.render(name_wordcloud.html)word_freq是前面Counter.most_common(20)的结果格式是[(word, count), ...]。word_size_range控制字体大小范围数值越大词云越夸张。5.3 用 Page 组合多个图表成一张大屏单独打开多个 HTML 文件太麻烦用Page把它们拼成一个页面from pyecharts.charts import Page page Page(layoutPage.DraggablePageLayout) page.add(bar, wc) page.render(dashboard.html)DraggablePageLayout允许你在浏览器里拖动图表位置调整好后点击“保存布局”会生成一个 JSON 文件下次加载时用Page.save_resize_html固定布局。这个功能在答辩演示时很实用不用现场调样式。6. 避坑与常见问题排查那些让我熬夜的报错6.1 返回 200 但解析不到任何数据现象resp.status_code是 200但html.xpath(//dd)返回空列表。原因猫眼识别出爬虫后返回了一个验证页面页面结构完全不同。解决检查 User-Agent 是否带了完整的 Chrome 标识加上Referer: https://www.maoyan.com/并把请求间隔调到 2 秒以上。6.2 评分字段拼接后转 float 报 ValueError现象float(score_int score_frac)抛出ValueError: could not convert string to float。原因某些条目的评分是“暂无评分”整数部分和小数部分取出来是空字符串。解决在拼接前判断如果score_int为空则跳过该条或者赋默认值 0.0。6.3 pyecharts 生成的 HTML 打开是空白现象render成功但浏览器打开只有白屏。原因pyecharts 2.x 默认使用 CDN 加载 echarts.js如果网络不通就渲染不出来。解决在set_global_opts里指定opts.InitOpts(js_host本地路径)或者把 echarts.min.js 下载到本地同目录修改 HTML 里的 script src。6.4 jieba 分词把“之”和“的”排到最前面现象词云里最大的词是“之”“的”“传”这类无意义字。原因片名里这类字出现频率确实高但分析价值低。解决维护一个停用词列表在jieba.lcut后过滤掉。stopwords {之, 的, 传, 记, 大, 小} all_words [w for w in all_words if w not in stopwords and len(w) 1]6.5 SQLite 写入中文乱码现象用 DB Browser 打开maoyan.db看到中文变成问号。原因SQLite 默认编码是 UTF-8但某些工具显示时用了 GBK。解决确认df.to_sql之前 DataFrame 里的中文没有经过错误编码转换用df.info()检查字段类型是否为 object。7. 从静态图表到可交互大屏一个让答辩加分的小技巧如果你想让答辩老师眼前一亮可以在 pyecharts 大屏基础上加一个下拉筛选器按评分区间动态过滤数据。pyecharts 本身不直接支持联动但可以用Page的DraggablePageLayout配合一点 JavaScript 实现。更简单的做法是用 pandas 先按评分区间拆成多个 DataFrame每个区间生成一套图表然后用Page的Tab组件做成标签页切换。from pyecharts.charts import Tab tab Tab() for label in labels: subset df[df[score_range] label] if subset.empty: continue bar_sub ( Bar() .add_xaxis(subset[name].tolist()[:10]) .add_yaxis(评分, subset[score].tolist()[:10]) .set_global_opts( title_optsopts.TitleOpts(titlef{label}区间电影评分), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate45)), ) ) tab.add(bar_sub, label) tab.render(tab_dashboard.html)Tab组件会在页面顶部生成一排标签点击切换不同区间的图表。rotate45是防止片名太长导致 X 轴标签重叠。这个技巧不需要额外的前端知识纯 Python 就能搞定。最后说一个我踩过的坑不要等到答辩前一天才跑完整流程。猫眼的页面结构偶尔会微调比如 class 名从board-index变成board-index-newXPath 就失效了。我一般会在爬取脚本里加一个断言如果解析到的条目数少于 5 条就打印警告并保存当前 HTML 到本地方便事后排查。这个习惯帮我省过好几次通宵改代码的时间。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询