当前位置: 首页 > news >正文

Python 爬虫案例:爬取豆瓣电影 Top250 数据

一、案例背景与目标

豆瓣电影 Top250 是国内权威的电影评分榜单之一,包含电影名称、评分、评价人数、导演、主演、上映年份、国家 / 地区、类型等关键信息。本案例将使用 Python 编写爬虫,实现以下目标:

  1. 自动请求豆瓣电影 Top250 的 10 个分页(每页 25 部电影);
  2. 解析页面 HTML 结构,提取每部电影的 8 项核心信息;
  3. 数据清洗(处理异常字符、统一格式);
  4. 将最终数据保存到 Excel 文件,方便后续分析。

二、技术栈选择

本案例使用轻量级且易上手的技术组合,适合爬虫初学者:

工具 / 库作用
requests发送 HTTP 请求,获取网页源代码
BeautifulSoup4解析 HTML 文档,提取目标数据(非结构化→结构化)
pandas数据清洗、整理,并将数据写入 Excel
time控制请求间隔,避免触发网站反爬机制
user-agent伪装浏览器请求头,绕过基础反爬

三、完整代码实现

# 1. 导入所需库
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time# 2. 定义核心配置
# 豆瓣电影 Top250 分页 URL 规律:start 参数从 0 开始,每次加 25(0、25、50...225)
BASE_URL = "https://movie.douban.com/top250?start={}&filter="
# 伪装浏览器请求头(避免被识别为爬虫)
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
}
# 存储所有电影数据的列表
movie_list = []# 3. 定义页面解析函数(提取单页电影数据)
def parse_movie_page(html):# 初始化 BeautifulSoup 解析器(指定 lxml 解析器,效率更高)soup = BeautifulSoup(html, "lxml")# 定位所有电影项的父容器(每个 li 对应一部电影)movie_items = soup.find_all("li", class_="item")for item in movie_items:# 3.1 提取电影名称(默认取第一个中文名称)title_tag = item.find("span", class_="title")movie_name = title_tag.get_text(strip=True) if title_tag else "未知名称"# 3.2 提取评分rating_tag = item.find("span", class_="rating_num")rating = rating_tag.get_text(strip=True) if rating_tag else "0.0"# 3.3 提取评价人数(处理格式,如 "123.4万人评价" → "1234000")comment_tag = item.find("span", text=lambda x: x and "人评价" in x)comment_count = comment_tag.get_text(strip=True).replace("人评价", "").replace("万", "0000") if comment_tag else "0"# 3.4 提取导演和主演(格式:"导演: 张艺谋 主演: 沈腾, 马丽")info_tag = item.find("div", class_="bd").find("p", class_="")info_text = info_tag.get_text(strip=True).split("\n") if info_tag else ["", ""]director_actor = info_text[0].strip() if len(info_text) > 0 else "未知信息"# 3.5 提取上映年份、国家/地区、类型(格式:"2023 / 中国大陆 / 喜剧, 剧情")detail_text = info_text[1].strip() if len(info_text) > 1 else "未知 / 未知 / 未知"detail_list = detail_text.split(" / ")release_year = detail_list[0].strip() if len(detail_list) > 0 else "未知年份"country = detail_list[1].strip() if len(detail_list) > 1 else "未知国家"genre = detail_list[2].strip() if len(detail_list) > 2 else "未知类型"# 3.6 提取电影简介(处理可能的空值)quote_tag = item.find("span", class_="inq")intro = quote_tag.get_text(strip=True) if quote_tag else "无简介"# 3.7 将单部电影数据存入字典movie_dict = {"电影名称": movie_name,"评分": rating,"评价人数": comment_count,"导演&主演": director_actor,"上映年份": release_year,"国家/地区": country,"类型": genre,"简介": intro}movie_list.append(movie_dict)# 4. 定义主爬虫函数(循环请求所有分页)
def crawl_douban_top250():# 循环 10 个分页(start=0,25,...,225)for page in range(10):start = page * 25url = BASE_URL.format(start)print(f"正在爬取第 {page+1} 页,URL:{url}")try:# 4.1 发送 GET 请求(添加超时控制,避免无限等待)response = requests.get(url, headers=HEADERS, timeout=10)# 4.2 检查请求是否成功(状态码 200 表示正常)response.raise_for_status()  # 若状态码非 200,抛出 HTTPError 异常# 4.3 解析当前页面数据parse_movie_page(response.text)# 4.4 控制请求间隔(1-2 秒),避免给服务器造成压力,降低反爬风险time.sleep(1.5)except requests.exceptions.RequestException as e:print(f"爬取第 {page+1} 页失败,错误原因:{str(e)}")continue  # 跳过失败页面,继续爬取下一页# 5. 定义数据保存函数(保存到 Excel)
def save_to_excel():if not movie_list:print("无数据可保存!")return# 5.1 将列表转换为 DataFrame(pandas 数据结构,便于处理)df = pd.DataFrame(movie_list)# 5.2 数据清洗:处理评价人数的数值格式(如 "123.4000" → 1234000)df["评价人数"] = pd.to_numeric(df["评价人数"].str.replace(".", ""), errors="coerce").fillna(0).astype(int)# 5.3 保存到 Excel(index=False 表示不保存行号)excel_path = "豆瓣电影Top250数据.xlsx"df.to_excel(excel_path, index=False, engine="openpyxl")print(f"数据已成功保存到:{excel_path}")print(f"共爬取到 {len(movie_list)} 部电影数据")# 6. 程序入口(执行爬虫流程)
if __name__ == "__main__":print("开始爬取豆瓣电影 Top250 数据...")crawl_douban_top250()  # 1. 爬取数据save_to_excel()        # 2. 保存数据print("爬取任务完成!")

四、代码解析(关键步骤拆解)

1. 环境准备(安装依赖库)

在运行代码前,需要确保所有必要的 Python 库都已正确安装。这些库分别承担不同的功能:

  • requests:用于发送 HTTP 请求,获取网页内容
  • beautifulsoup4:用于解析 HTML 文档,提取所需数据
  • pandas:用于数据处理和分析
  • openpyxl:作为 pandas 的依赖,用于写入 Excel 文件
  • lxml:作为 BeautifulSoup 的解析器,提供高效的 HTML 解析能力

安装命令:

pip install requests beautifulsoup4 pandas openpyxl lxml

提示:如果是在国内网络环境,建议使用国内镜像源加速安装,例如:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests beautifulsoup4 pandas openpyxl lxml

2. 反爬机制规避(核心细节)

网络爬虫需要尊重目标网站的规则,同时也要采取适当措施避免被网站识别并封锁。本代码中采用了多种反爬策略:

2.1 伪装请求头
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
}

这是最基础也最重要的反爬措施。网站服务器通过User-Agent字段识别访问者身份,默认情况下,requests库的请求头会显示为 "python-requests/xx.x.x",很容易被识别为爬虫。通过设置一个真实的浏览器User-Agent,可以模拟正常用户的浏览器访问。

你可以通过访问https://httpbin.org/get查看自己浏览器的User-Agent并替换。

2.2 控制请求频率
time.sleep(1.5)

这行代码的作用是在爬取完一页后暂停 1.5 秒再继续。短时间内发送大量请求是爬虫最明显的特征之一,通过添加合理的延迟,可以模拟人类浏览网页的行为,降低被网站识别的概率。

延迟时间可以根据实际情况调整,一般建议在 1-3 秒之间。对于反爬严格的网站,可能需要更长的延迟。

2.3 异常处理机制
try:# 发送请求的代码
except requests.exceptions.RequestException as e:print(f"爬取第 {page+1} 页失败,错误原因:{str(e)}")continue

这段异常处理代码可以捕获所有与请求相关的异常,包括网络连接错误、超时、HTTP 错误状态码等。当某一页爬取失败时,程序不会崩溃,而是会打印错误信息并继续爬取下一页,保证了程序的健壮性。

response.raise_for_status()方法会在 HTTP 请求返回错误状态码(4xx 或 5xx)时抛出异常,让我们能够及时发现并处理请求错误。

3. HTML 解析逻辑(如何定位数据)

解析 HTML 是爬虫的核心步骤,需要仔细分析网页结构,找到目标数据所在的位置。我们可以通过浏览器的开发者工具(F12)来查看网页的 HTML 结构。

3.1 分析网页结构

豆瓣电影 Top250 的页面结构具有一定的规律性:

  • 所有电影条目都包含在<ul class="grid_view">标签中
  • 每个电影条目对应一个<li class="item">标签
  • 每个条目中包含了电影的各种信息:名称、评分、评价人数等
3.2 提取电影列表
movie_items = soup.find_all("li", class_="item")

这行代码使用find_all方法查找所有class为 "item" 的li标签,每个标签对应一部电影的信息。返回的movie_items是一个列表,包含了当前页面所有电影的信息。

3.3 提取单个电影信息
3.3.1 提取电影名称
title_tag = item.find("span", class_="title")
movie_name = title_tag.get_text(strip=True) if title_tag else "未知名称"

电影名称位于class为 "title" 的span标签中。get_text(strip=True)方法用于获取标签内的文本内容,并去除前后的空白字符。通过if title_tag else "未知名称"的判断,可以处理标签不存在的情况,避免程序出错。

3.3.2 提取评分
rating_tag = item.find("span", class_="rating_num")
rating = rating_tag.get_text(strip=True) if rating_tag else "0.0"

评分信息位于class为 "rating_num" 的span标签中。同样使用了条件判断来处理可能的缺失情况。

3.3.3 提取评价人数
comment_tag = item.find("span", text=lambda x: x and "人评价" in x)
comment_count = comment_tag.get_text(strip=True).replace("人评价", "").replace("万", "0000") if comment_tag else "0"

评价人数的提取相对复杂一些,因为它没有特定的class名称。这里使用了一个 lambda 函数作为筛选条件,查找文本中包含 "人评价" 的span标签。

提取到文本后,还需要进行处理:

  • 去除 "人评价" 字符串
  • 将 "万" 转换为 "0000",以便后续转换为数字
3.3.4 提取导演和主演
info_tag = item.find("div", class_="bd").find("p", class_="")
info_text = info_tag.get_text(strip=True).split("\n") if info_tag else ["", ""]
director_actor = info_text[0].strip() if len(info_text) > 0 else "未知信息"

导演和主演信息位于class为 "bd" 的div标签下的第一个p标签中。这里的find("p", class_="")表示查找没有class属性的p标签。

获取文本后,使用split("\n")按换行符分割,取第一部分作为导演和主演信息。

3.3.5 提取上映年份、国家 / 地区、类型
detail_text = info_text[1].strip() if len(info_text) > 1 else "未知 / 未知 / 未知"
detail_list = detail_text.split(" / ")
release_year = detail_list[0].strip() if len(detail_list) > 0 else "未知年份"
country = detail_list[1].strip() if len(detail_list) > 1 else "未知国家"
genre = detail_list[2].strip() if len(detail_list) > 2 else "未知类型"

这部分信息位于上一步中分割得到的info_text的第二部分,格式为 "年份 / 国家 / 地区 / 类型"。我们使用split(" / ")按 "/" 分割,得到一个包含三个元素的列表,分别对应年份、国家 / 地区和类型。

每个字段都添加了条件判断,以处理可能的缺失情况,保证程序的稳定性。

3.3.6 提取电影简介
quote_tag = item.find("span", class_="inq")
intro = quote_tag.get_text(strip=True) if quote_tag else "无简介"

电影简介位于class为 "inq" 的span标签中。同样添加了条件判断,处理没有简介的情况。

3.3.7 存储电影数据
movie_dict = {"电影名称": movie_name,"评分": rating,"评价人数": comment_count,"导演&主演": director_actor,"上映年份": release_year,"国家/地区": country,"类型": genre,"简介": intro
}
movie_list.append(movie_dict)

将提取到的各项信息存入一个字典,然后将字典添加到movie_list列表中。这样处理后,movie_list将包含当前页面所有电影的信息。

4. 数据清洗与保存

4.1 数据转换
df = pd.DataFrame(movie_list)

使用 pandas 库将列表转换为 DataFrame,这是一种二维表格数据结构,便于进行数据处理和分析。

4.2 数据清洗
df["评价人数"] = pd.to_numeric(df["评价人数"].str.replace(".", ""), errors="coerce").fillna(0).astype(int)

这行代码对 "评价人数" 进行清洗和转换:

  • str.replace(".", ""):去除字符串中的小数点,处理 "123.4 万" 这种格式
  • pd.to_numeric(..., errors="coerce"):将字符串转换为数值类型,无法转换的将设为 NaN
  • fillna(0):将 NaN 值替换为 0
  • astype(int):转换为整数类型

经过这些处理,"评价人数" 字段将成为干净的整数,便于后续的统计分析。

4.3 保存到 Excel
excel_path = "豆瓣电影Top250数据.xlsx"
df.to_excel(excel_path, index=False, engine="openpyxl")

使用 pandas 的to_excel方法将数据保存到 Excel 文件:

  • index=False:表示不保存 DataFrame 的索引列
  • engine="openpyxl":指定使用 openpyxl 库作为引擎,支持.xlsx 格式

保存完成后,会打印保存路径和爬取到的电影数量,方便用户确认结果。

5. 主程序流程

if __name__ == "__main__":print("开始爬取豆瓣电影 Top250 数据...")crawl_douban_top250()  # 1. 爬取数据save_to_excel()        # 2. 保存数据print("爬取任务完成!")

这是程序的入口点,使用if __name__ == "__main__":确保只有在直接运行该脚本时才会执行以下代码,而在被导入为模块时不会执行。

五、运行结果与验证

  1. 运行代码:执行程序后,控制台会输出爬取进度(如 “正在爬取第 1 页...”);
  2. 生成文件:程序结束后,当前目录会生成 豆瓣电影Top250数据.xlsx 文件;
  3. 验证数据:打开 Excel 文件,可看到 250 行数据(若部分页面爬取失败,行数可能少于 250),列包含 “电影名称”“评分” 等 8 项信息,数据格式统一、无乱码。

六、拓展与注意事项

1. 功能拓展

  • 多线程爬取:使用 threading 或 concurrent.futures 库实现多线程,提升爬取速度(注意控制线程数,避免给服务器造成过大压力);
  • 数据可视化:用 matplotlib 或 seaborn 绘制评分分布直方图、类型占比饼图等;
  • 增量爬取:记录上次爬取的最后一部电影,下次只爬取更新的数据。

2. 注意事项

  • 遵守网站 robots 协议:豆瓣电影的 robots.txt(https://movie.douban.com/robots.txt)允许爬取 Top250 数据,但需控制频率;
  • 反爬升级应对:若出现 “验证码” 或 “403 禁止访问”,可尝试添加 Cookie(模拟登录状态)、使用代理 IP 池;
  • 法律风险:不得将爬取的数据用于商业用途,遵守《网络安全法》《数据安全法》等法律法规。

通过本案例,可掌握爬虫的核心流程(请求→解析→清洗→保存),理解 HTML 结构分析、反爬规避、数据处理的关键技巧,为后续爬取更复杂网站(如动态加载、需要登录的网站)打下基础。

http://www.xdnf.cn/news/1416367.html

相关文章:

  • 华为云CCE
  • 【Flask】测试平台开发,实现全局邮件发送工具 第十二篇
  • [免费]基于Python的气象天气预报数据可视化分析系统(Flask+echarts+爬虫) 【论文+源码+SQL脚本】
  • 【Proteus仿真】蜂鸣器控制系列仿真——蜂鸣器控制/蜂鸣器播放音乐/蜂鸣器播放多种音乐/蜂鸣器和LED组成报警装置
  • 如何在Github中创建仓库?如何将本地项目上传到GitHub中?
  • 【HTML】draggable 属性:解锁网页交互新维度
  • 深入探讨Java异常处理:受检异常与非受检异常的最佳实践
  • 领码方案:低代码平台前端缓存与 IndexedDB 智能组件深度实战
  • Eclipse Compiler for Java (ECJ):安装指南与高效快捷键全解析
  • 玩转OurBMC第二十一期:前端页面仪表盘的设计与使用实践
  • Trae x MCP:一键打造品牌专属高质量SVG封面
  • CompletableFuture初体验
  • (9.1)Python测试之记录
  • Shell 编程 —— 正则表达式与文本处理器
  • 函数,数组与正则表达式
  • Android原生HttpURLConnection上传图片方案
  • 打造智能写作工作流:n8n + 蓝耘MaaS平台完整实战指南
  • Apollo学习之决策模块
  • 【Linux手册】Unix/Linux 信号:原理、触发与响应机制实战
  • Ajax笔记(下)
  • 在.NET标准库中进行数据验证的方法
  • Java视觉跟踪入门:使用OpenCV实现实时对象追踪
  • 【开题答辩全过程】以 基于php的校园兼职求职网站为例,包含答辩的问题和答案
  • 【Android】使用Handler做多个线程之间的通信
  • 【Flask】测试平台开发,应用管理模块实现-第十一篇
  • 【lucene核心】impacts的由来
  • 旧物回收小程序:科技赋能,开启旧物新生之旅
  • 山东省信息技术应用创新开展进程(一)
  • 《C++进阶之STL》【红黑树】
  • OS+MySQL+(其他)八股小记