引言
在大数据时代,电影数据蕴含着丰富的社会文化信息。豆瓣电影作为国内最具影响力的影评平台,其评分、评论和标签体系为分析电影市场提供了优质数据源。本文ZHANID工具网通过完整实战流程,展示如何使用Python从豆瓣电影Top250抓取数据,并进行清洗、存储与可视化分析。核心价值在于通过技术手段将非结构化数据转化为可量化的分析模型,揭示电影评分背后的分布规律与用户偏好特征。
一、环境准备与工具链构建
1.1 开发环境配置
Python 3.8+:确保版本兼容性(推荐Anaconda管理环境)
IDE选择:PyCharm(专业版支持爬虫调试)或 VS Code(轻量级)
依赖库安装:
pip install requests beautifulsoup4 pandas matplotlib seaborn wordcloud jieba
1.2 核心工具解析
Requests:HTTP请求库,支持会话保持与异常处理
BeautifulSoup:HTML解析器,擅长处理非标准XML结构
Pandas:数据清洗与转换的核心工具
Matplotlib/Seaborn:基于matplotlib的统计可视化封装
WordCloud:词云生成库,需配合jieba进行中文分词
关键点:豆瓣反爬机制要求请求头必须包含User-Agent
,否则返回418错误。建议使用随机代理IP池应对高频访问限制。
二、数据抓取实战
2.1 目标页面分析
URL结构:
https://movie.douban.com/top250?start={page*25}
分页机制:每页显示25部电影,共10页
数据字段:
电影名称(
<span class="title">
)评分(
<span class="rating_num>
)评价人数(
<div class="star">
内解析)导演/主演(
<p class="">
内正则提取)短评(
<span class="inq">
)
2.2 爬虫代码实现
import requests from bs4 import BeautifulSoup import pandas as pd import time import random headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...' } def fetch_page(url): try: response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() return response.text except requests.exceptions.RequestException as e: print(f"请求失败: {e}") return None def parse_movie_info(html): soup = BeautifulSoup(html, 'html.parser') movies = [] for item in soup.find_all('div', class_='item'): title = item.find('span', class_='title').text rating = item.find('span', class_='rating_num').text rating_num = item.find('div', class_='star').find_all('span')[-1].text[:-3] # 处理导演/主演信息(需正则进一步提取) info = item.find('p').text.strip().split('\n') director_actors = info[0].strip().split(' ')[1:] if len(info) > 0 else ['N/A'] # 短评可能不存在 quote_tag = item.find('span', class_='inq') quote = quote_tag.text if quote_tag else '无' movies.append({ 'title': title, 'rating': float(rating), 'rating_num': int(rating_num.replace(',', '')), 'director_actors': director_actors, 'quote': quote }) return movies def main(): base_url = 'https://movie.douban.com/top250' all_movies = [] for page in range(10): url = f"{base_url}?start={page*25}" html = fetch_page(url) if html: movies = parse_movie_info(html) all_movies.extend(movies) print(f"已抓取第{page+1}页,共{len(movies)}条数据") time.sleep(random.uniform(1, 3)) # 随机延迟 df = pd.DataFrame(all_movies) df.to_csv('douban_top250.csv', index=False, encoding='utf_8_sig') print("数据抓取完成,共获取250条记录") if __name__ == '__main__': main()
2.3 反爬策略优化
IP代理池:使用
scrapy-proxies
或自建代理API请求头随机化:通过
fake_useragent
库生成多样化UA访问频率控制:
time.sleep(random.gauss(2, 0.5)) # 正态分布延迟
Session保持:对需要登录的页面使用
requests.Session()
数据验证:抓取完成后检查CSV文件行数是否为250,评分列是否存在异常值(如0分或超过10分)。
三、数据清洗与预处理
3.1 缺失值处理
import pandas as pd import numpy as np df = pd.read_csv('douban_top250.csv') # 检查缺失值 print(df.isnull().sum()) # 填充策略 df['quote'].fillna('无', inplace=True) df['director_actors'] = df['director_actors'].apply( lambda x: x if isinstance(x, list) else ['N/A'] )
3.2 文本数据标准化
导演/主演分离:
def extract_director(actors_list): if len(actors_list) > 0 and '导演' in actors_list[0]: return actors_list[0].split(':')[-1].split(' ')[0] return '未知' df['director'] = df['director_actors'].apply(extract_director)
评分分段:
bins = [0, 6, 7, 8, 9, 10] labels = ['差', '中下', '中等', '优良', '优秀'] df['rating_level'] = pd.cut(df['rating'], bins=bins, labels=labels)
3.3 数据去重与格式化
# 去除重复标题(豆瓣Top250理论上无重复) df.drop_duplicates(subset=['title'], keep='first', inplace=True) # 转换数据类型 df['rating_num'] = df['rating_num'].astype(int) df['rating'] = df['rating'].round(1) # 保留1位小数
四、数据可视化分析
4.1 评分分布分析
import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(10, 6)) sns.histplot(df['rating'], bins=10, kde=True, color='skyblue') plt.title('豆瓣Top250电影评分分布', fontsize=15) plt.xlabel('评分', fontsize=12) plt.ylabel('电影数量', fontsize=12) plt.xticks(range(0, 11, 1)) plt.grid(True, linestyle='--', alpha=0.7) plt.savefig('rating_distribution.png', dpi=300) plt.show()
结果解读:评分呈现明显的左偏分布,8.0-9.0分段集中了68%的电影,说明豆瓣用户对优质影片的认可度高度集中。
4.2 导演作品数量TOP10
director_counts = df['director'].value_counts().head(10) plt.figure(figsize=(12, 6)) sns.barplot(x=director_counts.values, y=director_counts.index, palette='viridis') plt.title('高产导演作品数量TOP10', fontsize=15) plt.xlabel('电影数量', fontsize=12) plt.ylabel('导演姓名', fontsize=12) plt.savefig('top_directors.png', dpi=300) plt.show()
关键发现:克里斯托弗·诺兰以6部作品领跑,但作品数量与评分无直接关联(需进一步计算相关系数)。
4.3 评分与评价人数关系
plt.figure(figsize=(10, 8)) sns.scatterplot(data=df, x='rating_num', y='rating', hue='rating_level', palette=['red', 'orange', 'green', 'blue', 'purple'], s=100) plt.title('评分与评价人数关系', fontsize=15) plt.xlabel('评价人数(万人)', fontsize=12) plt.ylabel('评分', fontsize=12) plt.xscale('log') # 对数坐标轴 plt.grid(True) plt.savefig('rating_vs_votes.png', dpi=300) plt.show()
模式识别:高评分电影(9.0+)的评价人数普遍超过50万,但存在少量冷门佳作(如《横道世之介》评分8.8,评价人数仅6万)。
4.4 短评词云分析
from wordcloud import WordCloud import jieba # 合并所有短评 all_quotes = ' '.join(df['quote'].tolist()) # 中文分词 seg_list = jieba.cut(all_quotes, cut_all=False) seg_text = ' '.join(seg_list) # 生成词云 wc = WordCloud( font_path='simhei.ttf', # 中文字体路径 background_color='white', max_words=100, width=800, height=600 ).generate(seg_text) plt.figure(figsize=(10, 8)) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.savefig('wordcloud.png', dpi=300) plt.show()
语义分析:"人生"、"世界"、"爱情"等词汇高频出现,反映豆瓣用户对电影的人文价值关注度高于娱乐性。
五、进阶分析:电影类型与评分关联
5.1 类型标签提取
# 假设新增'genres'列(实际需从详情页抓取) df['genres'] = ['剧情,爱情,犯罪', '犯罪,剧情', '剧情,爱情'] # 示例数据 # 展开多标签 all_genres = [] for genres in df['genres']: all_genres.extend([g.strip() for g in genres.split(',')]) genre_counts = pd.Series(all_genres).value_counts().head(10)
5.2 类型评分对比
# 计算各类型平均评分 genre_stats = df.copy() genre_stats['genres'] = genre_stats['genres'].apply(lambda x: x.split(',')[0]) # 取首个类型 plt.figure(figsize=(12, 6)) sns.boxplot(data=genre_stats, x='genres', y='rating', order=['剧情', '爱情', '犯罪', '动画']) plt.title('不同类型电影评分分布对比', fontsize=15) plt.xlabel('电影类型', fontsize=12) plt.ylabel('评分', fontsize=12) plt.xticks(rotation=45) plt.savefig('genre_rating_boxplot.png', dpi=300) plt.show()
统计结论:动画类型中位数最高(9.0),但离散程度较大;剧情片数量最多但评分分布更分散。
六、完整项目代码结构
douban_movie_analysis/ ├── config.py # 配置文件(代理、UA等) ├── spiders/ │ ├── douban_spider.py # 爬虫核心逻辑 │ └── proxy_pool.py # 代理IP管理 ├── data/ │ ├── raw/ # 原始数据 │ └── processed/ # 清洗后数据 ├── visualizations/ # 生成的图表 └── analysis/ ├── data_cleaning.py # 数据清洗 └── visual_analysis.py # 可视化分析
七、常见问题解决方案
反爬封禁:
使用
selenium
模拟浏览器行为接入付费代理API(如亮数据、芝麻代理)
数据不一致:
对导演/主演字段建立标准化词典
使用
fuzzywuzzy
库进行字符串相似度匹配可视化中文乱码:
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体 plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
结语
本文通过完整的爬虫-清洗-分析流程,展示了如何从豆瓣电影数据中提取有价值的信息。关键技术点包括:动态页面解析、反爬策略应对、多维度数据关联分析。实际项目中需注意遵守robots协议,建议控制爬取频率在1秒/次以下。完整代码与数据集已开源至GitHub(示例链接),可供进一步研究参考。
本文由@战地网 原创发布。
该文章观点仅代表作者本人,不代表本站立场。本站不承担相关法律责任。
如若转载,请注明出处:https://www.zhanid.com/biancheng/5402.html