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 页面截图)
五、 避坑指南:为什么我的爬虫跑不起来?
- 报错 418: 这是最常见的。说明你的 User-Agent 被识别了。请打开浏览器 F12,复制你本机的真实 User-Agent。
- 数据抓取不全: 豆瓣翻页是从 start=0, 25, 50... 开始的,请检查循环逻辑。
- 编码问题: 保存 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设置。")
如果你觉得这篇文章有用,点个赞就是对我最大的鼓励!如有报错,欢迎在评论区贴出,我看到必回。