Python实战:豆瓣电影数据爬取与可视化分析完整项目指南

📅 2026/7/15 2:06:15
Python实战:豆瓣电影数据爬取与可视化分析完整项目指南
这次我们来看一个完整的Python实战项目豆瓣电影数据爬取与可视化分析。这个项目结合了网络爬虫、数据处理和数据可视化三大技术模块是Python学习者从入门到实战的经典案例。项目核心价值在于通过实际可运行的代码教会你如何合法合规地获取豆瓣电影数据并进行有意义的可视化分析。整个过程不需要高端硬件普通电脑就能运行代码结构清晰适合Python初学者和有一定基础想要实战提升的开发者。1. 项目核心能力速览能力项具体说明数据来源豆瓣电影TOP250、电影详情页、影评数据技术栈Python Requests BeautifulSoup Pandas Matplotlib/Seaborn硬件要求普通PC即可无需独立显卡核心功能数据爬取、数据清洗、数据分析、可视化图表生成输出形式CSV数据文件、多种统计图表、分析报告适合人群Python初学者、数据分析爱好者、毕业设计参考2. 项目适用场景与价值这个项目特别适合以下几种情况学习实践场景如果你刚学完Python基础语法想找一个完整的项目来巩固知识体系这个项目涵盖了从数据获取到结果展示的全流程。数据分析入门想要进入数据分析领域但缺乏实战经验。通过这个项目可以掌握数据处理的完整流程。毕业设计参考很多计算机相关专业的学生需要完成数据分析类的毕业设计这个项目提供了完整的技术框架。个人兴趣探索对电影市场感兴趣想了解豆瓣评分规律、电影类型分布等有趣的信息。重要提醒爬虫程序必须遵守网站robots.txt协议控制访问频率避免对目标网站造成压力。本项目仅用于学习目的不建议大规模商业化使用。3. 环境准备与依赖安装3.1 Python环境要求推荐使用Python 3.7及以上版本。可以通过以下命令检查Python版本python --version # 或 python3 --version3.2 必要库安装创建新的虚拟环境后安装以下依赖库pip install requests beautifulsoup4 pandas matplotlib seaborn jupyter各库的作用说明requests用于发送HTTP请求获取网页内容beautifulsoup4解析HTML页面提取所需数据pandas数据处理和分析的核心库matplotlib基础绘图库seaborn基于matplotlib的高级可视化库jupyter交互式编程环境方便调试和展示3.3 开发工具推荐VSCode安装Python插件支持代码调试Jupyter Notebook适合分步执行和结果展示PyCharm专业的Python IDE4. 项目架构设计整个项目分为四个主要模块4.1 数据爬取模块负责从豆瓣电影获取原始数据包括电影基本信息标题、评分、导演、主演等电影详情数据时长、类型、上映日期等用户评论数据可选需谨慎使用4.2 数据清洗模块对爬取的数据进行预处理处理缺失值格式标准化数据类型转换去重处理4.3 数据分析模块进行各种统计分析评分分布分析类型统计时间趋势分析导演/演员作品统计4.4 可视化模块生成多种图表柱状图、饼图折线图、散点图热力图、词云图5. 核心代码实现5.1 豆瓣电影TOP250爬取import requests from bs4 import BeautifulSoup import pandas as pd import time import random class DoubanMovieSpider: def __init__(self): self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } self.base_url https://movie.douban.com/top250 def get_movie_list(self): 获取TOP250电影列表 movies [] for page in range(0, 250, 25): # 每页25条共10页 url f{self.base_url}?start{page} try: response requests.get(url, headersself.headers) soup BeautifulSoup(response.text, html.parser) items soup.find_all(div, class_item) for item in items: movie_info self.parse_movie_item(item) if movie_info: movies.append(movie_info) # 礼貌爬取添加延迟 time.sleep(random.uniform(1, 3)) print(f已爬取第{page//25 1}页累计{len(movies)}部电影) except Exception as e: print(f第{page//25 1}页爬取失败: {e}) continue return movies def parse_movie_item(self, item): 解析单个电影信息 try: # 提取电影标题 title_div item.find(div, class_hd) title title_div.find(span, class_title).text.strip() # 提取评分信息 rating_div item.find(div, class_star) rating float(rating_div.find(span, class_rating_num).text) rating_count rating_div.find_all(span)[-1].text.replace(人评价, ) # 提取基本信息 info_div item.find(div, class_bd) info info_div.find(p, class_).text.strip() director_actor info.split(\n)[0].strip() year_country_type info.split(\n)[1].strip().split(/) movie_info { title: title, rating: rating, rating_count: rating_count, director_actor: director_actor, year: year_country_type[0].strip(), country: year_country_type[1].strip() if len(year_country_type) 1 else , type: year_country_type[2].strip() if len(year_country_type) 2 else } return movie_info except Exception as e: print(f解析电影信息失败: {e}) return None # 使用示例 if __name__ __main__: spider DoubanMovieSpider() movies spider.get_movie_list() # 保存到CSV df pd.DataFrame(movies) df.to_csv(douban_top250.csv, indexFalse, encodingutf-8-sig) print(f数据保存完成共{len(movies)}部电影)5.2 数据清洗与处理import pandas as pd import re class DataCleaner: def __init__(self, file_path): self.df pd.read_csv(file_path) def clean_data(self): 数据清洗主函数 # 检查缺失值 print(缺失值统计:) print(self.df.isnull().sum()) # 处理评分人数转换为数值 self.df[rating_count] self.df[rating_count].str.replace(人评价, ).astype(int) # 提取年份中的数字 self.df[year] self.df[year].str.extract((\d{4})).astype(int) # 处理国家信息取第一个国家 self.df[country] self.df[country].str.split( ).str[0] # 处理电影类型取主要类型 self.df[main_type] self.df[type].str.split( ).str[0] # 从导演演员信息中提取导演 self.df[director] self.df[director_actor].str.split(主演).str[0].str.replace(导演: , ) return self.df def get_basic_stats(self): 获取基本统计信息 stats { total_movies: len(self.df), avg_rating: self.df[rating].mean(), max_rating: self.df[rating].max(), min_rating: self.df[rating].min(), avg_year: self.df[year].mean(), unique_countries: self.df[country].nunique(), unique_types: self.df[main_type].nunique() } return stats # 使用示例 cleaner DataCleaner(douban_top250.csv) cleaned_df cleaner.clean_data() stats cleaner.get_basic_stats() print(数据清洗完成) print(基本统计信息:) for key, value in stats.items(): print(f{key}: {value})5.3 数据分析与可视化import matplotlib.pyplot as plt import seaborn as sns from wordcloud import WordCloud import jieba plt.rcParams[font.sans-serif] [SimHei] # 解决中文显示问题 plt.rcParams[axes.unicode_minus] False class MovieVisualizer: def __init__(self, df): self.df df def plot_rating_distribution(self): 绘制评分分布图 plt.figure(figsize(10, 6)) plt.hist(self.df[rating], bins20, alpha0.7, colorskyblue, edgecolorblack) plt.xlabel(评分) plt.ylabel(电影数量) plt.title(豆瓣TOP250电影评分分布) plt.grid(True, alpha0.3) plt.savefig(rating_distribution.png, dpi300, bbox_inchestight) plt.show() def plot_year_trend(self): 绘制年份趋势图 year_count self.df[year].value_counts().sort_index() plt.figure(figsize(12, 6)) plt.plot(year_count.index, year_count.values, markero, linewidth2, markersize4) plt.xlabel(年份) plt.ylabel(电影数量) plt.title(豆瓣TOP250电影年份分布趋势) plt.grid(True, alpha0.3) plt.xticks(rotation45) plt.savefig(year_trend.png, dpi300, bbox_inchestight) plt.show() def plot_type_distribution(self): 绘制类型分布饼图 type_count self.df[main_type].value_counts().head(8) plt.figure(figsize(10, 8)) plt.pie(type_count.values, labelstype_count.index, autopct%1.1f%%, startangle90) plt.title(豆瓣TOP250电影类型分布) plt.axis(equal) plt.savefig(type_distribution.png, dpi300, bbox_inchestight) plt.show() def plot_rating_vs_year(self): 绘制评分与年份关系散点图 plt.figure(figsize(12, 8)) plt.scatter(self.df[year], self.df[rating], alpha0.6, cself.df[rating], cmapviridis) plt.colorbar(label评分) plt.xlabel(年份) plt.ylabel(评分) plt.title(电影评分与年份关系) plt.grid(True, alpha0.3) plt.savefig(rating_vs_year.png, dpi300, bbox_inchestight) plt.show() def create_wordcloud(self): 创建电影标题词云 text .join(self.df[title].tolist()) wordlist jieba.cut(text) word_text .join(wordlist) wordcloud WordCloud( font_pathsimhei.ttf, width800, height600, background_colorwhite, max_words100 ).generate(word_text) plt.figure(figsize(10, 8)) plt.imshow(wordcloud, interpolationbilinear) plt.axis(off) plt.title(豆瓣TOP250电影标题词云) plt.savefig(title_wordcloud.png, dpi300, bbox_inchestight) plt.show() def generate_all_charts(self): 生成所有图表 self.plot_rating_distribution() self.plot_year_trend() self.plot_type_distribution() self.plot_rating_vs_year() self.create_wordcloud() # 使用示例 visualizer MovieVisualizer(cleaned_df) visualizer.generate_all_charts()6. 高级数据分析功能6.1 导演作品分析def analyze_director_performance(df): 分析导演表现 director_stats df.groupby(director).agg({ title: count, rating: [mean, max, min], year: [min, max] }).round(2) director_stats.columns [movie_count, avg_rating, max_rating, min_rating, first_year, last_year] director_stats director_stats.sort_values(movie_count, ascendingFalse) # 筛选有2部以上作品的导演 productive_directors director_stats[director_stats[movie_count] 2] print(高产导演统计:) print(productive_directors.head(10)) return productive_directors director_stats analyze_director_performance(cleaned_df)6.2 国家电影质量分析def analyze_country_performance(df): 分析各国电影表现 country_stats df.groupby(country).agg({ title: count, rating: mean }).round(2).sort_values(title, ascendingFalse) country_stats.columns [movie_count, avg_rating] plt.figure(figsize(12, 6)) plt.bar(country_stats.head(10).index, country_stats.head(10)[movie_count]) plt.xlabel(国家) plt.ylabel(电影数量) plt.title(TOP250中各国电影数量分布) plt.xticks(rotation45) plt.tight_layout() plt.show() return country_stats country_stats analyze_country_performance(cleaned_df)7. 项目部署与运行指南7.1 完整项目结构douban_movie_analysis/ │ ├── spiders/ │ ├── __init__.py │ ├── douban_spider.py # 爬虫主程序 │ └── utils.py # 工具函数 │ ├── analysis/ │ ├── __init__.py │ ├── data_cleaner.py # 数据清洗 │ └── visualizer.py # 可视化 │ ├── data/ │ ├── raw/ # 原始数据 │ └── processed/ # 处理后的数据 │ ├── results/ │ └── charts/ # 生成的图表 │ ├── config.py # 配置文件 ├── main.py # 主程序入口 └── requirements.txt # 依赖列表7.2 一键运行脚本# main.py from spiders.douban_spider import DoubanMovieSpider from analysis.data_cleaner import DataCleaner from analysis.visualizer import MovieVisualizer import pandas as pd import os def main(): 主函数一键运行完整流程 # 创建必要的目录 os.makedirs(data/raw, exist_okTrue) os.makedirs(data/processed, exist_okTrue) os.makedirs(results/charts, exist_okTrue) print(开始豆瓣电影数据爬取...) # 第一步数据爬取 spider DoubanMovieSpider() movies spider.get_movie_list() raw_df pd.DataFrame(movies) raw_df.to_csv(data/raw/douban_top250_raw.csv, indexFalse, encodingutf-8-sig) print(f爬取完成共获取{len(movies)}部电影数据) # 第二步数据清洗 print(开始数据清洗...) cleaner DataCleaner(data/raw/douban_top250_raw.csv) cleaned_df cleaner.clean_data() cleaned_df.to_csv(data/processed/douban_top250_cleaned.csv, indexFalse, encodingutf-8-sig) print(数据清洗完成) # 第三步可视化分析 print(开始生成可视化图表...) visualizer MovieVisualizer(cleaned_df) visualizer.generate_all_charts() print(图表生成完成) # 第四步生成分析报告 generate_report(cleaned_df) print(分析报告生成完成) print(项目运行完毕) def generate_report(df): 生成简单的分析报告 report f 豆瓣TOP250电影分析报告 生成时间{pd.Timestamp.now().strftime(%Y-%m-%d %H:%M:%S)} 基本统计信息 - 电影总数{len(df)} 部 - 平均评分{df[rating].mean():.2f} 分 - 最高评分{df[rating].max()} 分 - 最低评分{df[rating].min()} 分 - 时间跨度{df[year].min()}年 - {df[year].max()}年 - 涉及国家{df[country].nunique()} 个 - 电影类型{df[main_type].nunique()} 种 主要发现 1. 评分分布大部分电影评分集中在8.5-9.0分之间 2. 年代分布2000年后的电影占据较大比例 3. 类型分布剧情片是TOP250中的主要类型 4. 国家分布中国和美国电影占据主导地位 with open(results/analysis_report.txt, w, encodingutf-8) as f: f.write(report) if __name__ __main__: main()8. 常见问题与解决方案8.1 爬虫访问限制问题问题现象请求被拒绝返回403错误解决方案# 1. 使用更真实的User-Agent 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 } # 2. 添加请求延迟 import time import random time.sleep(random.uniform(2, 5)) # 随机延迟2-5秒 # 3. 使用IP代理如需大规模爬取 proxies { http: http://your_proxy:port, https: https://your_proxy:port }8.2 中文编码问题问题现象保存的CSV文件中文乱码解决方案# 保存时指定编码 df.to_csv(filename.csv, indexFalse, encodingutf-8-sig) # 读取时指定编码 df pd.read_csv(filename.csv, encodingutf-8-sig)8.3 可视化图表中文显示问题问题现象图表中的中文显示为方框解决方案import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号8.4 依赖库版本冲突问题现象代码在不同环境运行结果不一致解决方案使用requirements.txt固定版本requests2.28.1 beautifulsoup44.11.1 pandas1.5.0 matplotlib3.6.0 seaborn0.12.0 jieba0.42.1 wordcloud1.8.2.29. 项目扩展与进阶方向9.1 数据源扩展爬取更多页面的电影数据如豆瓣电影分类页面获取用户评论数据进行情感分析结合其他电影网站数据进行比较分析9.2 分析维度扩展添加票房数据对比分析分析电影时长与评分的关系研究导演/演员的合作网络时间序列分析电影类型流行趋势9.3 技术升级使用Scrapy框架重构爬虫添加数据库存储MySQL/MongoDB开发Web可视化界面Flask/Django使用Plotly/Dash创建交互式图表9.4 部署优化添加定时任务自动更新数据使用Docker容器化部署添加日志记录和错误监控实现数据增量更新10. 伦理与合规使用指南10.1 爬虫伦理准则尊重robots.txt遵守豆瓣的爬虫协议控制访问频率避免对服务器造成压力仅限学习使用不用于商业用途保护用户隐私不收集个人敏感信息10.2 数据使用边界分析结果仅用于学习研究不对外提供原始数据下载图表展示时进行聚合处理引用数据时注明来源这个项目最大的价值在于提供了一个完整的数据分析实战框架。你可以基于这个基础根据自己的兴趣和需求进行各种扩展。比如添加更多的数据维度尝试不同的可视化方式或者将分析结果应用到实际业务场景中。建议先从基础版本开始运行确保每个模块都能正常工作然后再逐步添加新的功能。遇到问题时可以优先查看常见问题部分大多数技术问题都有对应的解决方案。