1. 为什么我们需要爬取豆瓣电影Top250?
作为一个电影爱好者兼Python开发者,我经常需要获取电影数据进行分析。豆瓣电影Top250榜单汇集了全球最受欢迎的250部电影,包含了丰富的元数据:评分、评论人数、导演、演员、类型、上映年份等。这些数据对于电影推荐系统、市场分析、学术研究都具有重要价值。
手动收集这些数据显然不现实。想象一下,你需要打开250个电影页面,逐个复制粘贴信息,这至少要花费10-15个小时。而使用Python爬虫,我们可以在几分钟内自动化完成这项工作,效率提升近百倍。
注意:爬取数据时请遵守豆瓣的robots.txt规定,合理控制请求频率,避免对服务器造成过大压力。
2. 环境准备与工具选型
2.1 Python环境配置
我推荐使用Python 3.8+版本,这是目前最稳定且兼容性最好的版本。可以通过以下命令检查你的Python版本:
bash复制python --version
如果你还没有安装Python,可以从官网下载安装包。安装时务必勾选"Add Python to PATH"选项,这样可以在命令行直接运行Python。
2.2 必备库安装
我们将使用以下Python库:
- requests:发送HTTP请求
- BeautifulSoup:解析HTML
- pandas:数据处理和存储
- lxml:BeautifulSoup的解析器
使用pip安装这些库:
bash复制pip install requests beautifulsoup4 pandas lxml
2.3 开发工具选择
我强烈推荐使用VS Code作为开发环境。它轻量、免费,且有优秀的Python插件支持。安装后需要配置Python环境:
- 安装Python扩展
- 设置Python解释器路径
- 启用代码自动补全
3. 基础爬虫实现
3.1 分析网页结构
首先打开豆瓣电影Top250页面(https://movie.douban.com/top250),右键"检查"查看网页源码。我们发现:
- 每页显示25部电影
- 共10页
- 每部电影信息包含在
<div class="item">中 - 电影标题在
<span class="title"> - 评分在
<span class="rating_num">
3.2 发送HTTP请求
使用requests库获取网页内容:
python复制import requests
url = "https://movie.douban.com/top250"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
}
response = requests.get(url, headers=headers)
print(response.status_code)
提示:设置User-Agent很重要,否则可能被识别为爬虫而拒绝访问。
3.3 解析HTML内容
使用BeautifulSoup解析获取的HTML:
python复制from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, "lxml")
movie_items = soup.find_all("div", class_="item")
for item in movie_items[:3]: # 只查看前3部电影
title = item.find("span", class_="title").text
rating = item.find("span", class_="rating_num").text
print(f"电影: {title}, 评分: {rating}")
4. 处理分页与完整数据抓取
4.1 分页逻辑实现
豆瓣Top250有10页,每页URL格式为:
https://movie.douban.com/top250?start={offset},其中offset从0开始,每次增加25。
python复制base_url = "https://movie.douban.com/top250?start={}"
movies = []
for page in range(10):
url = base_url.format(page * 25)
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "lxml")
for item in soup.find_all("div", class_="item"):
# 提取数据代码...
movies.append(movie_data)
print(f"已抓取第{page+1}页")
time.sleep(3) # 礼貌性延迟
4.2 完整数据提取
除了标题和评分,我们还可以提取更多信息:
python复制def parse_movie(item):
data = {}
data["title"] = item.find("span", class_="title").text
data["rating"] = float(item.find("span", class_="rating_num").text)
data["votes"] = int(item.find("div", class_="star").find_all("span")[-1].text[:-3])
data["year"] = item.find("div", class_="bd").find("p").text.split("\n")[2].strip()[:4]
data["director"] = item.find("div", class_="bd").find("p").text.split("\n")[1].split("主演")[0].strip()[3:]
return data
5. 反爬机制与应对策略
5.1 常见反爬措施
豆瓣采用了多种反爬手段:
- User-Agent检测
- IP频率限制
- Cookie验证
- 动态加载内容
5.2 应对方案
5.2.1 请求头设置
除了User-Agent,还应该设置合理的请求头:
python复制headers = {
"User-Agent": "Mozilla/5.0...",
"Accept": "text/html,application/xhtml+xml...",
"Accept-Language": "zh-CN,zh;q=0.9",
"Referer": "https://movie.douban.com/",
}
5.2.2 请求频率控制
避免短时间内发送过多请求:
python复制import random
import time
def random_delay():
time.sleep(random.uniform(1, 3))
5.2.3 使用代理IP
当遇到429错误时,可以考虑使用代理IP:
python复制proxies = {
"http": "http://your_proxy:port",
"https": "http://your_proxy:port",
}
response = requests.get(url, headers=headers, proxies=proxies)
6. 数据存储与分析
6.1 存储到CSV
使用pandas将数据保存为CSV:
python复制import pandas as pd
df = pd.DataFrame(movies)
df.to_csv("douban_top250.csv", index=False, encoding="utf_8_sig")
6.2 简单数据分析
我们可以进行一些基本分析:
python复制# 评分最高的10部电影
top10 = df.sort_values("rating", ascending=False).head(10)
# 各年份电影数量
year_counts = df["year"].value_counts().sort_index()
# 导演作品数量
director_counts = df["director"].value_counts().head(10)
7. 进阶技巧与优化
7.1 异步爬取
使用aiohttp实现异步请求,大幅提高爬取速度:
python复制import aiohttp
import asyncio
async def fetch_page(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession(headers=headers) as session:
tasks = [fetch_page(session, base_url.format(i*25)) for i in range(10)]
pages = await asyncio.gather(*tasks)
# 处理页面...
7.2 使用Selenium处理动态内容
对于JavaScript渲染的内容,可以使用Selenium:
python复制from selenium import webdriver
driver = webdriver.Chrome()
driver.get(url)
soup = BeautifulSoup(driver.page_source, "lxml")
# 解析逻辑...
driver.quit()
7.3 数据持久化到数据库
使用SQLAlchemy存储到SQLite数据库:
python复制from sqlalchemy import create_engine
engine = create_engine("sqlite:///movies.db")
df.to_sql("douban_movies", engine, if_exists="replace", index=False)
8. 实际项目中的经验分享
在长期使用这个爬虫的过程中,我积累了一些宝贵经验:
- 请求失败处理:实现自动重试机制,对于失败的请求进行3次重试
- 增量爬取:记录已爬取的电影ID,避免重复爬取
- 数据校验:检查获取的数据是否完整有效
- 日志记录:详细记录爬取过程,便于排查问题
- 异常处理:捕获各种可能的异常,确保程序不会意外终止
一个健壮的生产级爬虫应该包含所有这些要素。我在GitHub上维护了一个完整版的实现,包含了错误处理、日志记录、配置管理等完整功能。
