1. 项目概述
"Python爬虫实战:一文搞定,一键采集豆瓣电影Top 250(附 CSV 导出)!"这个标题直指三个核心需求:用Python实现爬虫技术、采集特定结构化数据、实现数据持久化存储。作为Python入门后最具成就感的实战项目之一,爬取豆瓣Top250电影榜单既能学习网络请求、HTML解析等基础技能,又能获得可直接用于数据分析的规整数据集。
我在2016年第一次尝试这个项目时,豆瓣的反爬机制还比较简单。随着网站防护升级,现在需要更专业的请求头设置和访问频率控制。本文将分享经过多次迭代后的稳定方案,包含最新反爬绕过技巧和数据处理优化方法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心需求解析
2.1 数据采集目标
豆瓣Top250页面包含以下关键字段:
- 电影名称(含原名/译名)
- 导演/主演信息
- 上映年份/国家
- 类型标签
- 评分(含评分人数)
- 经典台词(部分电影)
- 影片海报URL
这些数据分布在列表页和详情页,需要设计多级抓取策略。实测显示完整采集250条记录约需15-20分钟(含合理延时)。
2.2 技术栈选型
python复制基础工具链:
requests # 网络请求
BeautifulSoup # HTML解析
re # 正则表达式
pandas # 数据导出
进阶方案(防封禁):
fake_useragent # 随机UA
proxy_pool # IP代理池
selenium # 动态渲染
选择requests+BS4组合因其学习曲线平缓,适合首次接触爬虫的开发者。当遇到403封禁时,再逐步引入随机UA和代理机制。
3. 完整实现流程
3.1 页面结构分析
打开豆瓣Top250页面,观察URL规律:
code复制第一页:https://movie.douban.com/top250?start=0
第二页:https://movie.douban.com/top250?start=25
...
页码计算:start=(n-1)*25
每页25条记录,电影卡片包含在<div class="item">节点中。关键数据分布位置:
- 标题:
<span class="title"> - 评分:
<span class="rating_num"> - 基本信息:
<div class="bd">中的第一个<p>
3.2 基础爬虫实现
python复制import requests
from bs4 import BeautifulSoup
import time
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
def scrape_page(url):
time.sleep(3) # 遵守爬虫礼仪
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, '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
info = item.find('div', class_='bd').p.get_text(strip=True)
movies.append([title, rating, info])
return movies
3.3 反爬对抗策略
当频繁出现403错误时,需要升级防护措施:
- 动态UserAgent:
python复制from fake_useragent import UserAgent
ua = UserAgent()
headers['User-Agent'] = ua.random
- 请求间隔随机化:
python复制import random
time.sleep(random.uniform(1, 5))
- 代理IP池(需自行搭建或购买服务):
python复制proxies = {
'http': 'http://your_proxy:port',
'https': 'https://your_proxy:port'
}
response = requests.get(url, headers=headers, proxies=proxies)
4. 数据清洗与导出
4.1 结构化处理
原始采集的数据需要拆分信息字段:
python复制import re
def parse_info(info_str):
# 示例:"1994 / 美国 / 犯罪 剧情"
pattern = r'(\d{4})[\s\/]*([^\s\/]+)[\s\/]*(.*)'
match = re.match(pattern, info_str)
return match.groups() if match else (None, None, None)
4.2 CSV导出优化
使用pandas处理能自动处理编码问题:
python复制import pandas as pd
df = pd.DataFrame(movies, columns=['title', 'rating', 'year', 'country', 'genres'])
df.to_csv('douban_top250.csv', index=False, encoding='utf_8_sig') # 解决Excel中文乱码
重要提示:避免在循环中频繁写入文件,应先在内存中完成所有数据处理,最后一次性导出。
5. 常见问题排查
5.1 请求被封禁
- 现象:连续返回403状态码
- 解决方案:
- 检查headers是否完整(需包含Referer)
- 增加随机延时(建议2-5秒)
- 更换IP地址(4G热点或代理服务)
5.2 数据缺失
- 现象:部分字段获取为空
- 调试方法:
python复制with open('debug.html', 'w', encoding='utf-8') as f:
f.write(response.text)
检查保存的HTML文件,确认目标元素是否存在class变化
5.3 编码问题
- 现象:CSV文件打开乱码
- 解决方案:
- 使用
utf_8_sig编码 - 用文本编辑器(如VSCode)转换编码
- 避免使用WPS直接打开(推荐LibreOffice)
- 使用
6. 项目扩展方向
- 数据可视化:
python复制import matplotlib.pyplot as plt
df['year'] = df['year'].astype(int)
df.groupby('year').size().plot(kind='bar')
- 评分分析:
python复制df['rating'] = df['rating'].astype(float)
print(f"平均分:{df['rating'].mean():.1f}")
- 词云生成(需安装wordcloud):
python复制from wordcloud import WordCloud
text = ' '.join(df['genres'])
wc = WordCloud(font_path='msyh.ttc').generate(text)
wc.to_file('genres_cloud.png')
这个项目最实用的经验是:永远先小规模测试解析逻辑(比如先处理1页数据),确认无误后再扩展为全量爬取。我在初期曾因直接爬取全部页面,导致在解析到第38条记录时因未处理的特殊格式而崩溃,不得不重新开始。
