Python猫眼电影爬虫实战:从数据采集到可视化毕设全流程

发布时间:2026/9/16 23:23:49
Python猫眼电影爬虫实战:从数据采集到可视化毕设全流程 简介一份面向高校计算机相关专业毕业设计、课程设计的Python猫眼电影数据分析实战资源覆盖数据爬取、清洗、存储到可视化全流程尤其涉及字体反爬破解等实际难点适合毕设参考或爬虫进阶学习。压缩包共77个文件约4.79MB以Python源码为核心辅以HTML可视化报告、JSON/DB数据库文件、图片素材、配置文件及设计文档各类型分工明确便于按模块阅读运行还附有运行环境与依赖说明。目前已有69人学习可借鉴其项目框架与实现思路。内容涵盖完整可运行的爬虫与可视化代码、猫眼电影2018年票房/评分/产地等分析图表以及配套说明文档从项目搭建到功能完善均有清晰记录还可在此基础上扩展其他题材性价比高适合需要快速落地相关课设或毕设的读者。读者可通过爬虫采集、数据入库、可视化展示的完整链路掌握真实的爬虫反破解思路与数据分析方法亦可为课程答辩或项目展示提供素材。1. 从爬虫到可视化猫眼电影毕设的全链路价值毕业设计里能同时覆盖 Python 爬虫、数据分析和可视化三个方向的选题不多猫眼电影 Top100 榜单是其中少有的“一片顶三片”。榜单结构固定、字段完整——排名、片名、主演、上映时间、评分都能直接从静态 HTML 里取不需要处理复杂的 Cookie 流程数据量又控制在 100 条级别爬下来的数据放进 CSV 或 SQLite用 pandas 做什么分析都顺手。另一个常被忽略的点是这个题目自带业务场景评分分布、年份趋势、演员关联这些结论放回电影行业语境里都讲得通论文“研究意义”部分不用硬凑。我们直接从从业角度给出可复现的实现方案不依赖任何现成源码包把爬虫抓取、数据质量处理、图表输出到最终报告的全链路讲清楚新手照着做能跑通熟手也能看到字段清洗和反爬参数的取舍。2. Python 猫眼电影爬取的架构模块划分、存储选型与依赖管理2.1 requests 爬虫模块为什么不用 Scrapy 和 Selenium很多第一次做爬虫项目的人一上来就在 Scrapy 和 Selenium 之间纠结。Scrapy 作为一个完整的爬虫框架有 Downloader、Scheduler、Pipeline 一整条链路工程上确实规范但如果选题只是猫眼 Top100 这种单站单表的数据Scrapy 的目录结构本身就占了项目一半的复杂度调试成本全花在 Item 和 Pipeline 的流转上。Selenium 会把浏览器整个拉起来解析速度慢无头环境的配置也麻烦拿来做单页静态数据属于杀鸡用牛刀。所以这里直接选 requests。它是同步 HTTP 客户端配合正则或 BeautifulSoup 解析 HTML够用且直观。搜索热词里 Python 爬虫方向被提及最多的库就是 requests底层逻辑很简单拿 URL、带 headers、发 GET 请求、拿响应文本。先看一个最小请求写法import requests from fake_useragent import UserAgent ua UserAgent() def get_page(url): headers { User-Agent: ua.random, Referer: https://www.maoyan.com/, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, } response requests.get(url, headersheaders, timeout10) response.raise_for_status() response.encoding utf-8 return response.text这里ua.random的作用是每次请求换一个 UA 字符串让请求看起来来自不同浏览器版本Referer带上首页地址是模拟从站内点进榜单的路径。raise_for_status()在返回 4xx/5xx 时直接抛异常省去手动判断状态码的分支encoding显式设成 utf-8否则 requests 会按响应头里的编码猜测中文片名偶尔会出现乱码。100 条数据分 10 页拉同步请求加 sleep 整个跑完不到一分钟这也是这个项目不需要上异步或多线程的关键理由。2.2 SQLite 与 CSV 双存储怎么选以及完整目录结构存储上要讨论的是 CSV 和 SQLite 的取舍。CSV 的优点是人眼直接能看Excel 打开就能检查字段SQLite 的好处是查询方便、数据类型被强制约束适合论文里写“数据库设计”一节时直接贴 SQL。对于 100 条规模的数据CSV 完全够用所以最终方案是原始数据落 CSV分析过程从 CSV 读入内存如果指导教师要求数据库内容再写一个to_sql把 DataFrame 存入 SQLite两条路都走通报告里也能多写一块内容。maoyan_top100/ ├── data/ │ ├── maoyan.csv │ └── maoyan.db ├── src/ │ ├── crawler.py # 爬虫模块 │ ├── analysis.py # 数据分析模块 │ └── visualize.py # 可视化模块 ├── output/ │ ├── bar_year.html │ ├── pie_score.html │ └── wordcloud_actors.html ├── requirements.txt └── README.md目录按功能拆分不按文件类型拆。爬虫、分析、可视化三个模块各自独立后面写报告时每个章节正好对应一个模块也方便答辩时演示“三步运行”的流程。data 和 output 目录初始为空运行时自动生成README 里写清楚这一点评分老师打开项目不会一头雾水。2.3 requirements.txt 与运行顺序报告里怎么描述依赖毕设代码给别人的时候依赖清单比代码本身更重要。requirements.txt 至少要包含下表内容包名版本建议用途requests2.25发 HTTP 请求fake-useragent0.1.11UA 随机轮换beautifulsoup44.9备用 HTML 解析pandas1.3数据清洗与分析pyecharts1.9图表可视化jieba0.42中文分词词云图用jieba 很多人会漏掉。如果只统计演员名字的词频确实不需要分词但如果想把片名或短片简介丢进词云中文会被切成单个汉字生成效果很差。用jieba.lcut切一遍再统计成本只有一行代码效果完全不同。运行顺序是python crawler.py生成 data/maoyan.csv再python analysis.py输出统计指标最后python visualize.py生成 output/ 下的 HTML 图表。三个脚本独立运行报告里把这一条链路画成数据流图比贴大段代码更能体现工程思维。3. 猫眼 Top100 榜单爬取实现offset 翻页、请求头与正则解析3.1 榜单 URL 规则与页面结构分析猫眼 Top100 榜单的入口是“排行榜-更早榜单-经典榜”路径固定在/board/4。翻页不是常规的/page/2而是/board/4?offset0这种以榜单偏移量控制的方式offset0 是第一页offset10 是第二页依此类推每页 10 条。用浏览器开发者工具查看页面源码榜单里每部电影嵌在一个dd标签里字段结构固定字段HTML 特征排名i classboard-index board-index-11/i片名a href/films/123 title肖申克的救赎主演p classstar主演蒂姆·罗宾斯, 摩根·弗里曼/p上映时间p classreleasetime上映时间1994-09-10/p评分i classinteger9./ii classfraction7/i抓取的最佳策略是直接定位整个dd标签在这个片段里做字段提取而不是在整个页面里逐个 find class。前者逻辑清晰、不怕字段错位后者一旦某个电影缺字段后面的解析结果全都会串行。3.2 爬虫代码requests 请求、重试、UA 轮换与解析基于上述设计下面这份爬虫核心代码可以直接运行。它只用了 requests 同步请求加正则没有框架层依赖逐行解释起来也容易import requests import re import time import random import csv from fake_useragent import UserAgent def fetch_html(url, retries3): ua UserAgent() headers { User-Agent: ua.random, Referer: https://www.maoyan.com/board/4, } for attempt in range(1, retries 1): try: resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() resp.encoding utf-8 return resp.text except requests.RequestException as exc: print(f第 {attempt} 次请求失败: {exc}) time.sleep(2 * attempt) return None def parse_movies(html): result [] pattern re.compile( rdd.*?board-index.*?(\d)/i r.*?title(.*?) r.*?classstar主演(.*?)/p r.*?classreleasetime上映时间(.*?)/p r.*?classinteger(.*?)/ii classfraction(.*?)/i, re.S ) for match in pattern.finditer(html): rank, title, actors, release, score_int, score_frac match.groups() result.append({ rank: int(rank), title: title.strip(), actors: actors.strip(), release_time: release.strip(), score: float(score_int score_frac), }) return result def crawl_top100(): all_movies [] for offset in range(0, 100, 10): html fetch_html(fhttps://www.maoyan.com/board/4?offset{offset}) if html: all_movies.extend(parse_movies(html)) print(f已抓取 offset{offset}累计 {len(all_movies)} 条) time.sleep(random.uniform(1, 3)) return all_movies这段代码有两个容易出问题的地方。第一个是正则里的.*?非贪婪匹配只有配合re.S让.匹配换行才能跨行匹配dd之间的完整内容一旦漏掉re.Sfinditer 什么都拿不到且不会报错。第二个是评分字段猫眼把评分拆成整数部分和小数部分两个i标签必须先拼接再转 float直接取其中一个标签会导致评分只有个位数分析时会出现 9.0 分占满全表的假象。重试机制的time.sleep(2 * attempt)是线性退避第一次失败等 2 秒第二次等 4 秒。这个策略在答辩时会被问到解释成“避免连续失败时请求频率过快给服务器留恢复时间”即可。3.3 字段清洗与 CSV 落盘评分字段的坑直接抓下来的 actors 字段带有“主演”前缀和英文逗号release_time 带有“上映时间”前缀。清洗时要统一处理方便后续 pandas 拆分def clean_and_save(movies, csv_pathdata/maoyan.csv): for m in movies: m[actors] m[actors].replace(,, 、).strip() m[release_time] m[release_time].replace(上映时间, ).strip() with open(csv_path, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[rank, title, actors, release_time, score]) writer.writeheader() writer.writerows(movies) print(f已保存 {len(movies)} 条数据到 {csv_path})注意 CSV 要写入utf-8-sig而不是utf-8。前置 BOM 是给 Excel 打开用的不加 BOM 的中文 CSV 用 Excel 打开会乱码。这一步清洗逻辑单独写成函数答辩时能清晰指出数据质量处理的环节比笼统说“做了清洗”有说服力得多。4. 数据分析评分分布、年份趋势与主演词频的 pandas 实现4.1 读取与数据清洗类型转换、空值处理爬虫落盘到 CSV 之后pandas 读进来第一件事不是直接做图而是检查数据质量。数据质量决定后面分析有没有说服力。pd.read_csv读入后立刻看每列的 dtype 和缺失值import pandas as pd df pd.read_csv(data/maoyan.csv, encodingutf-8-sig) print(df.info()) print(df.isna().sum())df.info()会打印每列的非空数量与数据类型。实际跑下来release_time 是 object 类型必须用pd.to_datetime转成 datetime64后续按年聚合才能用.dt访问器。再把 score 用astype(float)做一次强转这行有一个隐藏作用如果 CSV 里某一行评分不是数字这里会直接抛 ValueError把坏数据在分析前拦下来。df[release_time] pd.to_datetime(df[release_time]) df[year] df[release_time].dt.year df[score] df[score].astype(float)4.2 三个分析维度以及对应代码这个项目最值得做的分析有三个评分分布、年份趋势、主演词频。评分分布看 Top100 的得分集中在哪个区间能直接反映榜单含金量年份趋势看每年上榜电影数量能说明榜单的时间跨度是否覆盖老片和新片主演词频把 100 部电影的主演全部拆开统计得到“上榜最多演员榜单”这是报告里最有故事性的一个表。# 评分分布描述性统计 score_desc df[score].describe() # 年份趋势 year_count df.groupby(year).size().sort_index(ascendingFalse) # 主演词频 actor_counter {} for actors in df[actors].str.split(、): for actor in actors: actor actor.strip() actor_counter[actor] actor_counter.get(actor, 0) 1 actor_df pd.Series(actor_counter).sort_values(ascendingFalse).head(20)分析代码的坑在str.split(、)这一步第 3 章清洗时如果只处理了英文逗号而漏掉全角逗号拆分出来的同一个演员会被统计成两个人。第 3 章 replace 的目标就是把逗号统一成顿号这里才能放心 split。.dt.year取年份后groupby 不会自动补全没有上榜电影的年份画图时横轴会有空洞后面会处理。4.3 分析结果的呈现方式与报告素材pandas 的 describe 输出适合直接复制进 Word 表格但标题要改写不能叫“describe 输出”。例如评分均值为 9.0、最高 9.7、最低 8.3 这种结论适合改写为一句业务语言“Top100 中超过六成影片评分在 9 分以上头部分化明显9.5 分以上属于绝对的经典阵营。”年份趋势的结果需要补全缺失年份再画图year_count year_count.reindex(range(1994, 2020), fill_value0)这里 range 的终点 2020 需要根据实际抓取数据调整如果榜单里有 2023 年的电影终点就取 2024。固定写死年份在答辩时会被问“为什么是 2020”回答“由数据范围确定脚本里可以改为df[year].min()和df[year].max()动态生成”会更严谨。这里得到的三个聚合结果正好决定下一章要选什么图表评分分布用饼图或直方图年份趋势用柱状图主演词频用词云。5. 可视化图表pyecharts 柱状图、饼图、词云与图形化大屏5.1 pyecharts 基础配置pyecharts 1.x 之后是链式调用风格每个 add 和 set_ 方法返回图表对象本身最后 render 成独立 HTML。这个 HTML 不需要后台服务双击就能在浏览器看效果对毕设来说是最省事的一环。不需要 Node、不需要 Nginx把 HTML 放进报告附录也没问题。from pyecharts.charts import Bar from pyecharts import options as opts bar ( Bar() .add_xaxis(year_count.index.tolist()) .add_yaxis(上榜数量, year_count.values.tolist()) .set_global_opts( title_optsopts.TitleOpts(titleTop100电影上榜年份分布), xaxis_optsopts.AxisOpts(name年份), yaxis_optsopts.AxisOpts(name数量), ) ) bar.render(output/bar_year.html)这里最容易踩的坑是add_xaxis要求传列表而year_count.index是 Index 对象必须显式tolist()否则某些 pyecharts 版本会报类型错误。values.tolist()同理。中文标题不需要额外配置字体pyecharts 生成的 HTML 页面会读本地系统字体。5.2 饼图、词云图的代码与参数说明饼图适合展示评分段占比。先把评分切成区间再统计各段数量bins [8, 8.5, 9, 9.5, 10] labels [8-8.5, 8.5-9, 9-9.5, 9.5-10] df[score_range] pd.cut(df[score], binsbins, labelslabels, rightFalse) range_count df[score_range].value_counts().sort_index() from pyecharts.charts import Pie pie ( Pie() .add(, [list(z) for z in zip(range_count.index.tolist(), range_count.values.tolist())]) .set_global_opts(title_optsopts.TitleOpts(title评分区间占比)) .set_series_opts(label_optsopts.LabelOpts(formatter{b}: {d}%)) ) pie.render(output/pie_score.html)add第二参数要求[(name1, value1), (name2, value2), ...]这种二元组列表直接用zip再包一层list是常见写法。formatter里的{b}是名称{d}是占比百分比{c}是原始数值这三个占位符在答辩时被问到的概率很高要能说清楚区别。词云图可以用 pyecharts 的 WordCloud 类from pyecharts.charts import WordCloud wordcloud ( WordCloud() .add(, actor_df.items(), word_size_range[20, 100], shapecircle) .set_global_opts(title_optsopts.TitleOpts(title上榜演员词云)) ) wordcloud.render(output/wordcloud_actors.html)actor_df.items()返回可迭代的元组对是 WordCloud 最直接的输入格式。word_size_range控制词的显示字号范围默认的 20-80 在高频词只有十几个时显得稀疏调到 20-100 后视觉层级更明显。shape参数支持 circle、diamond、triangle 等形状但不要用太花哨的轮廓词云的可读性比形状重要。5.3 图表导出与拼接可视化大屏上面三个图是单独的 HTML 文件报告里各自截图即可。如果指导教师想要“可视化大屏”的效果常见做法是用 pyecharts 的 Page 类把多个图合并到一个页面from pyecharts.charts import Page page Page(layoutPage.DraggablePageLayout) page.add(bar, pie, wordcloud) page.render(output/dashboard.html)生成后用鼠标拖拽调整布局再做一次page.save_resize_html把坐标固化成独立文件。这一步不需要写配置代码DraggablePageLayout 模式自带“保存布局”按钮。关于可视化大屏有两条建议第一不要把图表背景色堆得大红大紫电影榜单用深色底或纯白底都行保持统一第二不要在报告里硬上地图可视化Top100 榜单里没有地域字段硬凑省份分布做出来的地图没有业务含义答辩时反而会被问住。6. 把项目做成完整毕设报告结构、答辩要点与常见坑6.1 报告结构怎么匹配评分点高分报告不要求技术多难要求每个评分点都有对应章节。最稳的结构是摘要里写明“爬虫-清洗-分析-可视化”四段式第二章写需求分析与技术选型第三章写爬虫设计与实现包括反爬策略和重试机制第四章写数据清洗与统计分析第五章写可视化展示与业务结论。代码不要全文贴进正文附录放核心代码段正文引用运行结果截图这符合学校对“系统实现”章节的常见预期。6.2 答辩现场最容易被问的三个问题第一个问题“如果 100 条数据不够怎么办”回答方向是扩展到全站电影的方法把 offset 上限抬高、处理动态加载接口、再考虑更严格的反爬限制。这个问题考察的不是代码量而是有没有想过数据规模变化时的架构影响。第二个问题“为什么只设计了 5 个字段”要说明为了分析聚焦舍弃了评论数、票房等字段如果指导教师希望看到更多字段可以说明在爬虫正则里扩展字段的路径比如加一个review_count的正则分组。第三个问题“反爬具体是怎么做的”回答三个要点UA 随机、Referer 伪装、sleep 随机间隔。这三个点对应代码里三处细节比笼统说“用了代理”更实在也更符合实际项目里小规模爬取的真实策略。6.3 给代码“保鲜”的最后一招提交前把每个脚本都加上清晰的打印输出爬虫每次打印当前 offset 和累计条数分析模块打印描述性统计结果可视化模块在 render 后打印“已生成 XX.html”。这看起来不起眼但评分老师通常不会逐行读 pandas 代码能看到清晰的运行过程比看注释有效得多。答辩现场跑一遍三个脚本同时把控制台输出的清洗前后对比保留下来会比朗读代码更有说服力。数据爬取、数据分析、可视化这三段恰好对应运行时的三次输出整个项目的完成度就完整落在了一个可演示的流程上。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询