用requests抓豆瓣top250可行但需加user-agent等请求头并控制频率,否则返回403或空页;推荐用soup.select('ol.grid_view li')解析,本地调试应缓存html避免ip被限。

直接说结论:用 requests 抓豆瓣 Top250 是可行的,但默认请求会返回 403 或空页——因为豆瓣有反爬,必须加 User-Agent 和合理请求头,且不能高频轮询。
为什么直接 requests.get("https://movie.douban.com/top250") 拿不到数据
豆瓣服务器会检查请求头中的 User-Agent、Accept、Accept-Language 等字段。空头或默认 python-requests/2.x 会被直接拦截,返回 403 或一个无内容的页面(甚至跳转到验证码页)。
- 常见错误现象:
response.status_code == 403,或len(response.text) ,但没报错 - 真实浏览器访问时带的
User-Agent类似:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36... - 只加
User-Agent不够,建议至少补全Accept和Accept-Language - 示例最小可用请求头:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
}
如何分页抓取全部 250 条(不是一次请求)
豆瓣 Top250 是分页加载的,每页 25 条,共 10 页;URL 规律是 start=0、start=25、start=50…直到 start=225。注意参数名是 start,不是 page 或 offset。
- 起始偏移量是 0,不是 1;第 n 页对应
start=(n-1)*25 - 不要用
time.sleep(1)硬控节奏——豆瓣对短时间连续请求敏感,建议随机延时 1–3 秒 - 务必检查
response.status_code == 200和页面是否含<div class="item">,否则可能是被限流或返回了登录页 <li>推荐用 <code>for start in range(0, 250, 25):控制循环,别手算页码 - 安全做法:定位到主榜单容器
ol.grid_view,再取其下的li,避免误抓广告 - 每个电影的标题在
div.info > div.hd > a > span.title:first-child,注意用:first-child排除「又名」 - 评分可能为空(如未上映影片),需用
get() or "暂无"防止AttributeError - 如果用
BeautifulSoup,推荐soup.select('ol.grid_view li')而非泛匹配.item - 立即生效的缓解方式:每次请求更换
User-Agent(从列表里随机选),并加time.sleep(random.uniform(1.5, 3.5)) - 不建议用代理池——豆瓣会校验请求头与 IP 地理位置、TLS 指纹等,普通 HTTP 代理反而更容易被识别
- 真正稳定的替代方案是调用豆瓣公开 API(如
https://api.douban.com/v2/movie/top250),但该接口已关闭多年,目前无官方替代 - 最容易被忽略的一点:本地调试时反复运行脚本,IP 很快进观察名单——建议首次跑完后缓存 HTML 到本地文件,后续解析用本地文件而非重抓
解析电影条目时,XPath 或 CSS 选择器容易失效的原因
豆瓣 HTML 结构看似稳定,但实际存在两类干扰:一是广告位插入的伪 <div class="item">(比如“想看”入口),二是部分条目缺失评分、年份等字段。直接用 <code>response.css('.item') 或 response.xpath('//div[@class="item"]') 会多抓 1–2 条脏数据。
被封 IP 或频繁 403 后该怎么办
这不是代码写错了,而是行为触发了豆瓣风控。临时解法是换请求头 + 加延时,长期可靠方案需要更谨慎的设计。
真正卡住人的往往不是怎么写 XPath,而是某次请求突然变空、或者第 7 页开始全返回 403——这时候要先确认是不是 headers 少了字段,再查是不是 sleep 不够,最后才怀疑 selector 写错了。











