Python 爬虫实战:爬取豆瓣电影 Top250 数据并进行可视化分析

Python 爬虫实战:从零抓取豆瓣电影 Top250 并进行多维度可视化分析

摘要: 豆瓣电影 Top250 是爬虫初学者的必经之路,但随着反爬机制的升级,很多旧教程已经失效(报错 418)。本文将分享最新的爬虫逻辑,并直接使用 pyecharts 生成精美的交互式看板。本文所有代码直接公开,不设网盘下载,复制即可运行!


一、 项目背景与准备

我们要抓取的信息包括:电影中文名、评分、评价人数、上映年份、制片国家以及电影主题。

1. 环境安装

你需要安装以下库:

code Bash

downloadcontent_copy

expand_less

复制代码
pip install requests beautifulsoup4 pandas pyecharts

2. 突破反爬(关键)

豆瓣现在对爬虫非常敏感,如果你不设置 User-Agent,会直接返回 418。在代码中,我们将模拟真实浏览器的 Header。


二、 核心代码实现:爬虫部分

我们使用 requests 获取网页,BeautifulSoup 解析 HTML。为了防止被封 IP,我们加入了 time.sleep() 模拟人工点击。

code Python

downloadcontent_copy

expand_less

复制代码
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import random

def get_douban_data():
    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',
        'Host': 'movie.douban.com'
    }
    
    movie_list = []
    
    # 豆瓣一共10页,每页25个数据
    for i in range(0, 250, 25):
        url = f'https://movie.douban.com/top250?start={i}&filter='
        try:
            response = requests.get(url, headers=headers, timeout=10)
            if response.status_code == 200:
                soup = BeautifulSoup(response.text, 'html.parser')
                items = soup.find_all('div', class_='item')
                
                for item in items:
                    title = item.find('span', class_='title').get_text() # 获取片名
                    rating = item.find('span', class_='rating_num').get_text() # 获取评分
                    star = item.find('div', class_='star')
                    comment_num = star.find_all('span')[-1].get_text()[:-3] # 获取评价人数
                    
                    # 进阶信息解析(年份、国家)
                    info = item.find('div', class_='bd').p.get_text().strip()
                    info_list = info.split('\n')[-1].split('/')
                    year = info_list[0].strip()
                    country = info_list[1].strip()
                    
                    movie_list.append([title, rating, comment_num, year, country])
                    
                print(f"成功爬取第 {i//25 + 1} 页...")
                # 随机休眠 1-3 秒,防止被封
                time.sleep(random.uniform(1, 3))
            else:
                print(f"请求失败,状态码:{response.status_code}")
        except Exception as e:
            print(f"抓取过程中出现错误: {e}")
            
    # 转为 DataFrame
    df = pd.DataFrame(movie_list, columns=['电影名', '评分', '评价人数', '年份', '国家'])
    return df

# 执行爬虫
# df = get_douban_data()
# df.to_csv('douban_top250.csv', index=False, encoding='utf-8-sig')

三、 数据清洗与可视化分析

有了数据后,我们利用 pyecharts 生成交互式图表。这些图表在浏览器中打开是可以点击互动的。

1. 评分分布分析

code Python

downloadcontent_copy

expand_less

复制代码
from pyecharts.charts import Bar
from pyecharts import options as opts

def draw_rating_bar(df):
    # 统计各评分出现的次数
    rating_counts = df['评分'].value_counts().sort_index()
    
    bar = (
        Bar()
        .add_xaxis(rating_counts.index.tolist())
        .add_yaxis("电影数量", rating_counts.values.tolist())
        .set_global_opts(
            title_opts=opts.TitleOpts(title="豆瓣Top250评分分布"),
            xaxis_opts=opts.AxisOpts(name="评分"),
            yaxis_opts=opts.AxisOpts(name="数量")
        )
    )
    bar.render("rating_distribution.html")

2. 电影年份走势

通过折线图观察,哪一个年代产生的经典电影最多。


四、 运行效果展示

运行完整代码后,你会得到一个 douban_top250.csv 文件和几个 .html 可视化图表。

(此处建议插入你运行后的 CSV 数据表格截图)
(此处建议插入生成的 pyecharts 图表 HTML 页面截图)


五、 避坑指南:为什么我的爬虫跑不起来?

  1. 报错 418: 这是最常见的。说明你的 User-Agent 被识别了。请打开浏览器 F12,复制你本机的真实 User-Agent。
  2. 数据抓取不全: 豆瓣翻页是从 start=0, 25, 50... 开始的,请检查循环逻辑。
  3. 编码问题: 保存 CSV 时请使用 encoding='utf-8-sig',否则用 Excel 打开会乱码。

六、 总结与源码获取

本文实现了从爬取、清洗到可视化的全流程。在实际操作中,我们要严格遵守 robots.txt 协议,仅用于学术交流,请勿高频请求服务器。

-- coding: utf-8 --

"""

项目名称:豆瓣电影 Top250 爬虫及可视化

功能:自动抓取、数据清洗、CSV存储、交互式图表生成

环境依赖:pip install requests beautifulsoup4 pandas pyecharts

"""

import requests

from bs4 import BeautifulSoup

import pandas as pd

import time

import random

from pyecharts.charts import Bar, Pie, Line, Page

from pyecharts import options as opts

=== 1. 数据爬取模块 ===

def crawl_douban_top250():

print("开始爬取豆瓣电影Top250数据,请稍候...")

headers = {

'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',

'Host': 'movie.douban.com',

'Referer': 'https://movie.douban.com/'

}

复制代码
data_list = []

for i in range(0, 250, 25):
    url = f'https://movie.douban.com/top250?start={i}'
    try:
        res = requests.get(url, headers=headers, timeout=10)
        res.raise_for_status()
        soup = BeautifulSoup(res.text, 'html.parser')
        items = soup.find_all('div', class_='item')
        
        for item in items:
            # 提取基本信息
            title = item.find('span', class_='title').get_text()
            rating = item.find('span', class_='rating_num').get_text()
            comment_info = item.find('div', class_='star').find_all('span')[-1].get_text()
            comment_num = comment_info[:-3] # 提取数字部分
            
            # 提取年份和国家 (在 <p> 标签的第二行)
            info = item.find('div', class_='bd').p.get_text().strip()
            # 按照 '/' 分割并清洗数据
            parts = info.split('\n')[-1].split('/')
            year = parts[0].strip()
            country = parts[1].strip().split(' ')[0] # 只要第一个国家名
            
            data_list.append({
                '电影名': title,
                '评分': float(rating),
                '评价人数': int(comment_num),
                '年份': year,
                '国家': country
            })
        
        print(f"成功抓取第 {i//25 + 1} 页数据...")
        time.sleep(random.uniform(2, 4)) # 模拟真人,防止封IP
        
    except Exception as e:
        print(f"抓取第 {i//25 + 1} 页时出错: {e}")
        continue
        
return pd.DataFrame(data_list)

=== 2. 可视化模块 ===

def create_visuals(df):

print("正在生成可视化看板...")

复制代码
# 图表 1: 评分分布柱状图
rating_counts = df['评分'].value_counts().sort_index()
bar = (
    Bar()
    .add_xaxis(rating_counts.index.tolist())
    .add_yaxis("电影数量", rating_counts.values.tolist(), color="#5470c6")
    .set_global_opts(title_opts=opts.TitleOpts(title="1. 豆瓣Top250评分分布"))
)

# 图表 2: 电影产地分布饼图 (Top 10)
country_counts = df['国家'].value_counts().head(10)
pie = (
    Pie()
    .add("", [list(z) for z in zip(country_counts.index.tolist(), country_counts.values.tolist())])
    .set_global_opts(title_opts=opts.TitleOpts(title="2. 前十大电影产地占比"))
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
)

# 图表 3: 评价人数最多的前10部电影
top_comment = df.sort_values(by='评价人数', ascending=False).head(10)
bar_top = (
    Bar()
    .add_xaxis(top_comment['电影名'].tolist())
    .add_yaxis("评价人数", top_comment['评价人数'].tolist(), color="#91cc75")
    .set_global_opts(
        title_opts=opts.TitleOpts(title="3. 评价人数Top 10电影"),
        xaxis_opts=opts.AxisOpts(axislabel_opts={"rotate": 30})
    )
)

# 整合到一个 HTML 页面
page = Page(layout=Page.SimplePageLayout)
page.add(bar, pie, bar_top)
page.render("豆瓣数据看板.html")
print("可视化看板生成完毕!请查看文件:豆瓣数据看板.html")

=== 3. 主函数入口 ===

if name == "main ":

1. 爬取

movie_df = crawl_douban_top250()

复制代码
if not movie_df.empty:
    # 2. 存储为 CSV (utf-8-sig 解决Excel打开乱码)
    movie_df.to_csv('douban_movies_2026.csv', index=False, encoding='utf-8-sig')
    print("数据已成功保存至:douban_movies_2026.csv")
    
    # 3. 可视化
    create_visuals(movie_df)
    print("\n=== 全部任务完成! ===")
else:
    print("未抓取到数据,请检查网络或User-Agent设置。")

如果你觉得这篇文章有用,点个赞就是对我最大的鼓励!如有报错,欢迎在评论区贴出,我看到必回。

相关推荐
c++之路10 小时前
C++信号处理
开发语言·c++·信号处理
m0_4954964110 小时前
mysql处理复杂SQL性能_InnoDB优化器与MyISAM差异
jvm·数据库·python
forEverPlume11 小时前
PHP怎么使用Eloquent Attribute Composition属性组合_Laravel通过组合构建复杂属性【方法】
jvm·数据库·python
Aleeeeex11 小时前
RAG 那点事:从 8 份企业文档到能用的问答系统,全过程拆给你看
人工智能·python·ai编程
2301_8092047011 小时前
mysql在docker容器中如何部署_利用docker-compose快速启动
jvm·数据库·python
Legendary_00811 小时前
LDR6500:USB‑C DRP PD协议芯片技术详解与应用实践
c语言·开发语言
2301_8009769312 小时前
正则表达式
开发语言·python·正则表达式
故事还在继续吗12 小时前
C++20关键特性
开发语言·c++·c++20
码界奇点12 小时前
基于Python的新浪微博数据爬虫系统设计与实现
数据库·爬虫·python·毕业设计·新浪微博·源代码管理