Python爬虫实战:从豆瓣电影抓取到数据可视化分析

原创 2025-08-18 10:08:03编程技术
498

引言

在大数据时代,电影数据蕴含着丰富的社会文化信息。豆瓣电影作为国内最具影响力的影评平台,其评分、评论和标签体系为分析电影市场提供了优质数据源。本文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分)。

PYTHON.webp

三、数据清洗与预处理

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()

语义分析:"人生"、"世界"、"爱情"等词汇高频出现,反映豆瓣用户对电影的人文价值关注度高于娱乐性。

豆瓣电影TOP250.webp

五、进阶分析:电影类型与评分关联

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 # 可视化分析

七、常见问题解决方案

  1. 反爬封禁

    • 使用selenium模拟浏览器行为

    • 接入付费代理API(如亮数据、芝麻代理)

  2. 数据不一致

    • 对导演/主演字段建立标准化词典

    • 使用fuzzywuzzy库进行字符串相似度匹配

  3. 可视化中文乱码

    plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

结语

本文通过完整的爬虫-清洗-分析流程,展示了如何从豆瓣电影数据中提取有价值的信息。关键技术点包括:动态页面解析、反爬策略应对、多维度数据关联分析。实际项目中需注意遵守robots协议,建议控制爬取频率在1秒/次以下。完整代码与数据集已开源至GitHub(示例链接),可供进一步研究参考。

Python爬虫 数据可视化 豆瓣电影
THE END
战地网
频繁记录吧,生活的本意是开心

相关推荐

真实项目复盘:我是如何用Python完成一个数据可视化的?
在当今数据驱动的时代,数据可视化已成为分析与决策过程中不可或缺的一环。本文将复盘一个真实项目案例,讲述我如何使用Python这一强大工具,从数据清洗、处理到最终可视化呈...
2025-09-03 编程技术
529

Python爬虫进阶:基于BeautifulSoup的链接分析与过滤方法
BeautifulSoup作为Python生态中最成熟的HTML解析库,其基于DOM树的解析模型和灵活的查询接口,为链接分析提供了强大的工具链。本文ZHANID工具网将系统阐述如何利用BeautifulS...
2025-07-08 编程技术
372

Python 数据可视化项目实战:南方暴雨洪涝数据分析
截至20250621,南方地区(特别是广东、广西、湖南等地)遭遇的极端暴雨和洪水灾害,斑点鱼将使用Python进行数据分析和可视化,展示洪水影响区域、雨势强度以及经济损失等情况。
2025-07-03 编程技术
420

Python爬虫抓取网页图片并下载保存到本地实例详解
在大数据时代,网络图片资源获取是数据采集的重要场景。无论是构建个人图库、进行图像分析还是搭建素材库,掌握网页图片抓取技术都能极大提升工作效率。本文ZHANID工具网将通...
2025-06-07 编程技术
742

Python Matplotlib 20种图表玩法:提升数据可视化能力
​本文将介绍 Python Matplotlib 的 20 种常见图表玩法,旨在帮助读者提升数据可视化的能力。通过这些图表类型,读者可以更好地展示和分析数据,从而在各种场景中做出更明智的...
2025-05-14 编程技术
610

掌握Python爬虫:使用XPath高效解析HTML文档
Python爬虫作为一种强大的数据采集工具,被广泛应用于各个领域。而在爬取网页内容时,解析HTML文档是一项必不可少的任务。传统的字符串操作方法不仅繁琐,而且容易出错。为了...
2024-12-03 编程技术
626