Python爬虫实战:猫眼电影票房数据采集与可视化分析完整指南 这次我们来看一个Python爬虫实战项目——爬取猫眼电影票房数据并进行可视化分析。这个案例特别适合Python初学者和想要掌握爬虫核心技能的朋友因为它涵盖了数据抓取、解析、存储和可视化的完整流程。猫眼电影作为国内重要的电影信息平台其票房数据对分析电影市场趋势很有价值。通过这个项目你可以学会如何用Python从网站获取结构化数据再用图表直观展示分析结果。整个过程不需要复杂的环境配置普通电脑就能跑起来。本文会带你完成从环境准备到数据可视化的全流程重点解决几个关键问题如何绕过常见的反爬机制、如何解析动态加载的数据、如何设计合理的请求频率避免被封IP以及如何用matplotlib和pandas做直观的数据图表。每个步骤都会提供可运行的代码示例和效果验证方法。1. 核心能力速览能力项说明技术栈Python requests BeautifulSoup pandas matplotlib数据源猫眼电影票房榜单主要功能数据抓取、数据解析、数据存储、可视化分析硬件要求普通电脑即可无需GPU核心难点反爬虫处理、数据解析、请求频率控制输出形式CSV数据文件、柱状图、折线图等多种图表适合场景爬虫学习、数据分析练习、电影市场研究2. 适用场景与使用边界这个爬虫项目最适合Python爬虫入门学习。如果你是刚学完Python基础语法想找一个有实际价值的练手项目这个案例非常合适。它涵盖了爬虫的核心技术点发送HTTP请求、解析HTML、处理反爬机制、数据清洗和存储。在实际应用层面这个工具可以用于电影市场趋势分析竞品电影票房对比节假日档期票房研究导演或演员作品票房统计但需要注意使用边界仅限个人学习和研究使用必须控制请求频率避免对目标网站造成压力不得用于商业用途或大规模数据采集遵守网站的robots.txt协议尊重数据版权和网站服务条款3. 环境准备与前置条件开始之前确保你的电脑已经准备好以下环境操作系统要求Windows 10/11、macOS 10.14 或 Ubuntu 18.04至少4GB内存10GB可用磁盘空间Python环境配置# 检查Python版本需要3.7及以上 python --version # 如果没有安装Python从官网下载安装包 # 推荐使用Python 3.8或3.9版本必要库安装# 使用pip安装所需库 pip install requests beautifulsoup4 pandas matplotlib lxml # 如果下载速度慢可以使用国内镜像源 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests beautifulsoup4 pandas matplotlib lxml开发工具准备代码编辑器VSCode、PyCharm或Jupyter Notebook浏览器开发者工具用于分析网页结构网络抓包工具如浏览器F12开发者工具4. 爬虫核心代码实现4.1 网页结构分析首先我们需要分析猫眼电影票房页面的结构。打开猫眼电影专业版票房页面使用F12开发者工具查看网络请求和HTML结构。关键发现票房数据通常通过API接口返回而不是静态HTML需要模拟浏览器请求头避免被识别为爬虫数据格式为JSON便于解析4.2 请求头设置与反爬处理import requests import json import time import pandas as pd from datetime import datetime, timedelta class MaoyanSpider: def __init__(self): self.session requests.Session() # 设置请求头模拟浏览器访问 self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: https://piaofang.maoyan.com/dashboard, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Connection: keep-alive } self.base_url https://piaofang.maoyan.com/api def get_boxoffice_data(self, dateNone): 获取指定日期的票房数据 date格式: YYYY-MM-DD默认为当天 if date is None: date datetime.now().strftime(%Y-%m-%d) url f{self.base_url}/boxoffice/daily params { date: date, type: 0 # 0表示单日票房 } try: response self.session.get(url, headersself.headers, paramsparams, timeout10) response.raise_for_status() data response.json() if data.get(success): return data.get(data, {}).get(list, []) else: print(f获取数据失败: {data.get(msg)}) return [] except requests.exceptions.RequestException as e: print(f请求失败: {e}) return []4.3 数据解析与清洗def parse_movie_data(self, raw_data): 解析原始数据提取关键信息 movies [] for item in raw_data: movie_info { 排名: item.get(rank, 0), 电影名称: item.get(movieName, ), 综合票房(万): round(item.get(boxInfo, 0) / 10000, 2), # 转换为万元 票房占比: round(item.get(boxRate, 0) * 100, 2), # 转换为百分比 排片占比: round(item.get(showRate, 0) * 100, 2), 场均人次: item.get(avgShowView, 0), 上座率: round(item.get(avgViewBox, 0) * 100, 2), 累计票房(万): round(item.get(sumBoxInfo, 0) / 10000, 2), 上映天数: item.get(releaseInfo, {}).get(releaseDays, 0), 数据日期: item.get(date, ) } movies.append(movie_info) return movies4.4 多日数据采集与存储def get_multiple_days_data(self, start_date, days7): 获取连续多天的票房数据 all_data [] current_date datetime.strptime(start_date, %Y-%m-%d) for i in range(days): date_str current_date.strftime(%Y-%m-%d) print(f正在获取 {date_str} 的数据...) raw_data self.get_boxoffice_data(date_str) if raw_data: daily_movies self.parse_movie_data(raw_data) all_data.extend(daily_movies) # 重要添加延时避免请求过于频繁 time.sleep(2) current_date timedelta(days1) return all_data def save_to_csv(self, data, filenameNone): 将数据保存为CSV文件 if filename is None: filename fmaoyan_boxoffice_{datetime.now().strftime(%Y%m%d_%H%M%S)}.csv df pd.DataFrame(data) df.to_csv(filename, indexFalse, encodingutf-8-sig) print(f数据已保存到: {filename}) return filename5. 数据可视化分析5.1 单日票房TOP10可视化import matplotlib.pyplot as plt import seaborn as sns class DataVisualizer: def __init__(self): plt.rcParams[font.sans-serif] [SimHei] # 设置中文字体 plt.rcParams[axes.unicode_minus] False sns.set_style(whitegrid) def plot_daily_top10(self, data, date): 绘制单日票房TOP10柱状图 # 筛选指定日期的数据 daily_data [movie for movie in data if movie[数据日期] date] top10 sorted(daily_data, keylambda x: x[排名])[:10] if not top10: print(f未找到 {date} 的数据) return fig, (ax1, ax2) plt.subplots(1, 2, figsize(15, 6)) # 票房柱状图 movie_names [movie[电影名称] for movie in top10] box_office [movie[综合票房(万)] for movie in top10] bars ax1.bar(range(len(movie_names)), box_office, colorsns.color_palette(husl, 10)) ax1.set_title(f{date} 单日票房TOP10, fontsize14, fontweightbold) ax1.set_xlabel(电影名称) ax1.set_ylabel(票房(万元)) ax1.set_xticks(range(len(movie_names))) ax1.set_xticklabels(movie_names, rotation45, haright) # 在柱子上添加数值标签 for bar, value in zip(bars, box_office): ax1.text(bar.get_x() bar.get_width()/2, bar.get_height() max(box_office)*0.01, f{value:.1f}, hacenter, vabottom, fontsize9) # 票房占比饼图 box_rates [movie[票房占比] for movie in top10] ax2.pie(box_rates, labelsmovie_names, autopct%1.1f%%, startangle90) ax2.set_title(票房占比分布) plt.tight_layout() plt.savefig(fdaily_top10_{date}.png, dpi300, bbox_inchestight) plt.show()5.2 多日趋势分析def plot_trend_analysis(self, data, movie_name, days7): 绘制指定电影的多日票房趋势图 # 筛选指定电影的数据 movie_data [movie for movie in data if movie[电影名称] movie_name] movie_data sorted(movie_data, keylambda x: x[数据日期])[-days:] if len(movie_data) 2: print(f{movie_name} 的数据不足无法进行趋势分析) return dates [movie[数据日期] for movie in movie_data] box_office [movie[综合票房(万)] for movie in movie_data] rankings [movie[排名] for movie in movie_data] fig, (ax1, ax2) plt.subplots(2, 1, figsize(12, 8)) # 票房趋势图 ax1.plot(dates, box_office, markero, linewidth2, markersize8, color#FF6B6B) ax1.set_title(f{movie_name} {days}日票房趋势, fontsize14, fontweightbold) ax1.set_ylabel(票房(万元)) ax1.grid(True, alpha0.3) # 在折线图上添加数值标签 for i, (date, value) in enumerate(zip(dates, box_office)): ax1.annotate(f{value:.1f}, (date, value), textcoordsoffset points, xytext(0,10), hacenter, fontsize9) # 排名趋势图 ax2.plot(dates, rankings, markers, linewidth2, markersize8, color#4ECDC4) ax2.set_title(f{movie_name} {days}日排名变化) ax2.set_xlabel(日期) ax2.set_ylabel(排名) ax2.invert_yaxis() # 排名数值越小越好所以倒置Y轴 ax2.grid(True, alpha0.3) # 在排名图上添加数值标签 for i, (date, rank) in enumerate(zip(dates, rankings)): ax2.annotate(f第{rank}名, (date, rank), textcoordsoffset points, xytext(0,10), hacenter, fontsize9) plt.tight_layout() plt.savefig(ftrend_{movie_name}_{days}days.png, dpi300, bbox_inchestight) plt.show()5.3 综合对比分析def plot_comparison_analysis(self, data, date, top_n5): 多维度对比分析 daily_data [movie for movie in data if movie[数据日期] date] top_movies sorted(daily_data, keylambda x: x[排名])[:top_n] if not top_movies: return metrics [综合票房(万), 票房占比, 排片占比, 上座率] movie_names [movie[电影名称] for movie in top_movies] fig, axes plt.subplots(2, 2, figsize(15, 10)) axes axes.flatten() colors sns.color_palette(Set2, top_n) for i, metric in enumerate(metrics): values [movie[metric] for movie in top_movies] axes[i].bar(movie_names, values, colorcolors) axes[i].set_title(f{metric}对比) axes[i].set_xticklabels(movie_names, rotation45, haright) # 添加数值标签 for j, value in enumerate(values): axes[i].text(j, value max(values)*0.01, f{value:.1f}, hacenter, vabottom, fontsize9) plt.suptitle(f{date} 电影市场多维度分析, fontsize16, fontweightbold) plt.tight_layout() plt.savefig(fcomparison_analysis_{date}.png, dpi300, bbox_inchestight) plt.show()6. 完整流程测试与验证6.1 主程序入口def main(): 主函数完整的爬取和可视化流程 print( 猫眼电影票房数据爬虫启动 ) # 初始化爬虫 spider MaoyanSpider() visualizer DataVisualizer() # 获取最近7天的数据 start_date (datetime.now() - timedelta(days7)).strftime(%Y-%m-%d) print(f开始获取从 {start_date} 开始的票房数据...) all_data spider.get_multiple_days_data(start_date, days7) if not all_data: print(数据获取失败请检查网络连接或反爬设置) return print(f成功获取 {len(all_data)} 条电影票房数据) # 保存数据 csv_file spider.save_to_csv(all_data) # 数据可视化 latest_date max(set(movie[数据日期] for movie in all_data)) # 1. 最新单日TOP10可视化 print(f生成 {latest_date} TOP10 可视化图表...) visualizer.plot_daily_top10(all_data, latest_date) # 2. 选取一部电影进行趋势分析 if all_data: sample_movie all_data[0][电影名称] print(f生成 {sample_movie} 趋势分析图表...) visualizer.plot_trend_analysis(all_data, sample_movie, days7) # 3. 多维度对比分析 print(生成多维度对比分析图表...) visualizer.plot_comparison_analysis(all_data, latest_date) print( 爬虫任务完成 ) print(f数据文件: {csv_file}) print(可视化图表已保存到当前目录) if __name__ __main__: main()6.2 测试运行验证运行上述代码后你应该能看到以下输出和文件控制台输出验证 猫眼电影票房数据爬虫启动 开始获取从 2024-01-01 开始的票房数据... 正在获取 2024-01-01 的数据... 正在获取 2024-01-02 的数据... ... 成功获取 105 条电影票房数据 数据已保存到: maoyan_boxoffice_20240108_143022.csv 生成 2024-01-07 TOP10 可视化图表... 生成 《电影名称》 趋势分析图表... 生成多维度对比分析图表... 爬虫任务完成 生成文件验证maoyan_boxoffice_20240108_143022.csv- 原始数据文件daily_top10_2024-01-07.png- 单日TOP10图表trend_电影名称_7days.png- 趋势分析图表comparison_analysis_2024-01-07.png- 多维度对比图表7. 高级功能扩展7.1 实时数据监控def real_time_monitor(spider, visualizer, interval3600): 实时监控票房数据变化 interval: 监控间隔秒 import schedule import time def monitoring_job(): current_time datetime.now().strftime(%Y-%m-%d %H:%M:%S) print(f[{current_time}] 执行监控任务...) # 获取最新数据 latest_data spider.get_boxoffice_data() if latest_data: parsed_data spider.parse_movie_data(latest_data) # 这里可以添加数据变化报警逻辑 print(f当前票房冠军: {parsed_data[0][电影名称]} - {parsed_data[0][综合票房(万)]}万元) # 定时任务 schedule.every(interval).seconds.do(monitoring_job) print(f启动实时监控每 {interval} 秒执行一次) while True: schedule.run_pending() time.sleep(1)7.2 数据异常检测def detect_anomalies(data, window3, threshold2.0): 检测票房数据异常波动 anomalies [] # 按电影分组 movies {} for record in data: name record[电影名称] if name not in movies: movies[name] [] movies[name].append(record) for name, records in movies.items(): records.sort(keylambda x: x[数据日期]) # 计算移动平均和标准差 if len(records) window: box_office [r[综合票房(万)] for r in records] for i in range(window, len(box_office)): window_data box_office[i-window:i] mean sum(window_data) / window std (sum((x - mean) ** 2 for x in window_data) / window) ** 0.5 current box_office[i] if std 0 and abs(current - mean) threshold * std: anomalies.append({ 电影名称: name, 日期: records[i][数据日期], 异常值: current, 预期范围: f{mean-threshold*std:.1f} - {meanthreshold*std:.1f}, 偏离程度: abs(current - mean) / std }) return anomalies8. 常见问题与排查方法8.1 请求被拒绝或返回空数据问题现象返回状态码403或418返回数据为空或包含验证码页面排查步骤检查User-Agent是否有效验证Referer设置是否正确检查IP是否被暂时封禁查看返回的HTML内容判断具体原因解决方案# 更新请求头 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://piaofang.maoyan.com/, Accept: application/json, text/plain, */*, } # 添加请求延时 time.sleep(random.uniform(1, 3)) # 使用代理IP如果需要 proxies { http: http://your_proxy:port, https: https://your_proxy:port }8.2 数据解析错误问题现象JSON解析失败字段缺失或格式变化排查方法# 打印原始响应内容检查结构 print(response.text[:500]) # 查看前500字符 # 使用try-except处理解析错误 try: data response.json() except json.JSONDecodeError as e: print(fJSON解析错误: {e}) print(f响应内容: {response.text})8.3 可视化图表显示问题中文字体显示乱码# 解决方案明确设置中文字体 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, DejaVu Sans] plt.rcParams[axes.unicode_minus] False图表尺寸不合适# 调整图表尺寸 plt.figure(figsize(12, 8)) # 宽12英寸高8英寸 # 自动调整布局 plt.tight_layout()9. 最佳实践与使用建议9.1 爬虫伦理与合规使用尊重robots.txt检查目标网站的robots.txt文件遵守爬虫规则控制请求频率每次请求后添加1-3秒延时避免对服务器造成压力设置超时时间避免长时间等待设置合理的超时参数错误处理机制网络异常时自动重试但限制重试次数9.2 数据存储与管理# 建议的数据存储结构 data/ ├── raw/ # 原始数据 ├── processed/ # 处理后的数据 ├── charts/ # 生成的图表 └── logs/ # 运行日志 # 使用时间戳命名文件避免覆盖 filename fboxoffice_{datetime.now().strftime(%Y%m%d_%H%M%S)}.csv9.3 性能优化建议使用会话保持重复使用Session对象减少连接开销批量处理数据避免频繁的文件读写操作内存管理处理大量数据时使用生成器或分块处理异步请求如果需要高速采集考虑使用aiohttp进行异步请求这个猫眼电影票房爬虫项目涵盖了Python爬虫的核心技术点从基础请求发送到复杂数据分析都有涉及。通过这个案例你不仅能学会爬虫技术还能掌握数据分析可视化的完整流程。建议先运行基础版本理解每个环节的原理再逐步添加高级功能。实际使用时记得遵守爬虫伦理控制请求频率将学到的技术用在正当的学习和研究用途上。这个项目的代码结构清晰模块化程度高你可以很方便地修改和扩展功能比如添加新的数据源、优化可视化效果或者集成到更大的数据分析系统中。