Python极简爬虫实战:12行代码获取豆瓣短评数据
1. 项目背景与核心思路
去年帮朋友做影视数据分析时,需要快速获取《黑豹》的豆瓣短评数据。传统爬虫教程往往从零开始搭建完整框架,但对于这种轻量级需求,其实用Python标准库+requests就能12行代码搞定。这种极简爬虫特别适合临时数据采集需求,无需复杂环境配置,直接上手就能跑通。
核心原理是利用requests模拟浏览器请求,通过豆瓣短评接口获取JSON数据。这里有个关键技巧:豆瓣对未登录用户只开放前20页短评(每页20条),而登录后可爬取更多数据。不过要注意频率控制,实测连续请求超过10次/分钟会触发429错误。
2. 环境准备与工具选型
2.1 基础环境配置
推荐使用Python 3.6+版本,主要依赖两个库:
pip install requests pip install pandas # 非必须,用于数据整理选择requests库而非Scrapy等框架的原因:
- 轻量级:无需创建完整爬虫项目
- 学习成本低:API设计直观,适合新手
- 调试方便:直接打印响应内容
2.2 接口分析技巧
通过Chrome开发者工具抓包发现:
- 短评接口格式:
https://movie.douban.com/subject/[电影ID]/comments?start=[偏移量]&limit=20 - 《黑豹》电影ID:5360029
- 关键参数:
- start:分页偏移量(0,20,40...)
- limit:固定值20
注意:务必添加User-Agent头部,否则会返回403错误。实测可用的UA:
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
3. 核心代码实现与解析
3.1 基础爬取代码
import requests headers = {'User-Agent': 'Mozilla/5.0'} comments = [] for i in range(0, 200, 20): # 爬10页数据 url = f'https://movie.douban.com/subject/5360029/comments?start={i}' resp = requests.get(url, headers=headers) comments.extend([item['content'] for item in resp.json()['comments']]) print(f'已获取第{i//20+1}页数据')代码关键点解析:
range(0, 200, 20):控制爬取页数(200=10页×20条)resp.json():自动解析JSON响应- 列表推导式:快速提取评论内容
3.2 异常处理增强版
实际运行时会遇到各种异常,建议增加以下处理:
import time from random import uniform for i in range(0, 200, 20): try: resp = requests.get(url, headers=headers, timeout=10) resp.raise_for_status() # 检查HTTP错误 data = resp.json() if 'comments' not in data: print('可能触发反爬,尝试降低频率') break comments.extend([item['content'] for item in data['comments']]) time.sleep(uniform(0.5, 1.5)) # 随机延迟 except Exception as e: print(f'第{i//20+1}页出错:{str(e)}') break4. 反爬机制与应对策略
4.1 豆瓣的反爬措施
- 频率限制:单IP请求过快会返回429状态码
- User-Agent验证:缺失或不常见的UA会被拦截
- Cookie验证:部分接口需要携带登录态
- 行为分析:连续规律请求可能触发验证码
4.2 合规爬取建议
- 遵守robots.txt规则:
- 豆瓣允许爬取电影页面,但禁止商业用途
- 建议间隔请求至少1秒以上
- 使用代理IP池(需自行搭建):
proxies = {'http': 'http://your_proxy:port'} requests.get(url, proxies=proxies) - 获取公开API权限:
- 豆瓣开放平台提供正式API接口
- 需要申请API Key并遵守调用限制
5. 数据存储与分析示例
5.1 存储到CSV文件
import pandas as pd df = pd.DataFrame({'comment': comments}) df.to_csv('black_panther_comments.csv', index=False, encoding='utf-8-sig')5.2 简单词频分析
from collections import Counter import jieba # 中文分词库 text = ' '.join(comments) words = [w for w in jieba.cut(text) if len(w) > 1] # 过滤单字 word_counts = Counter(words).most_common(20) print('高频词Top20:', word_counts)6. 常见问题排查指南
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 返回403错误 | 缺少User-Agent或Cookie | 添加合法UA头 |
| 返回429错误 | 请求频率过高 | 增加延迟或使用代理 |
| JSON解析失败 | 接口返回HTML验证页 | 检查响应内容格式 |
| 数据缺失 | 页面结构变更 | 更新CSS选择器/XPath |
| 连接超时 | 网络问题或IP被封 | 更换网络环境 |
7. 进阶优化方向
登录态保持:使用session对象保存cookies
session = requests.Session() session.post(login_url, data=credentials) session.get(api_url) # 自动携带cookies异步爬取提升效率:
import aiohttp async with aiohttp.ClientSession() as session: async with session.get(url) as resp: data = await resp.json()使用Selenium应对动态渲染:
from selenium import webdriver driver = webdriver.Chrome() driver.get(url) comments = driver.find_elements_by_css_selector('.comment-item')
我在实际爬取时发现,工作日下午的请求成功率明显高于晚间高峰时段。建议在代码中添加自动重试机制,当连续3次请求失败后,暂停10分钟再继续。另外保存中间结果到文件也很重要,避免程序中断导致数据丢失。
