第一个爬虫程序:用 Requests+BeautifulSoup 抓取豆瓣电影 Top250
对于刚入门 Python 爬虫的新手来说,“抓取豆瓣电影 Top250” 是一个绝佳的入门项目 —— 它数据结构清晰、反爬压力小,且能覆盖爬虫核心流程(请求网页、解析数据、存储结果)。本文将带你从零开始,用Requests(发送 HTTP 请求)和BeautifulSoup(解析 HTML)实现这个爬虫,全程代码可复现,新手也能轻松上手。
一、前期准备:明确目标与搭建环境
在写代码前,我们需要先理清 “做什么” 和 “用什么工具”。
1. 明确爬取目标
我们要从豆瓣电影 Top250 页面(https://movie.douban.com/top250 )中提取以下关键信息,最终整理成结构化数据(如 CSV 表格):
- 电影名称(中文)
- 电影评分
- 评价人数
- 导演与主演
- 上映时间与地区
- 剧情简介
2. 搭建开发环境
需要安装两个核心 Python 库,直接用pip
命令即可:
- Requests:用于向豆瓣服务器发送 HTTP 请求,获取网页源代码。
- BeautifulSoup4:用于解析获取到的 HTML 代码,提取我们需要的数据。
打开终端 / 命令提示符,输入以下命令安装:
bash
pip install requests beautifulsoup4
如果提示 “pip 不是内部命令”,需先检查 Python 环境变量是否配置(新手建议直接安装 Anaconda,自带 pip 和常用库)。
二、分析目标网页:找到数据规律
在写爬虫前,必须先 “读懂” 网页 —— 知道数据藏在 HTML 的哪个位置,以及分页规律。
1. 分析分页规律
豆瓣电影 Top250 共 10 页,每页 25 部电影。我们观察 URL 就能发现规律:
- 第 1 页:https://movie.douban.com/top250?start=0&filter= (
start=0
表示从第 0 部开始) - 第 2 页:https://movie.douban.com/top250?start=25&filter= (
start=25
表示从第 25 部开始) - 第 3 页:https://movie.douban.com/top250?start=50&filter=
- ...
- 第 10 页:https://movie.douban.com/top250?start=225&filter= (
start=225
,225+25=250)
因此,只需循环修改start
参数(0、25、50...225),就能爬取所有页面。
2. 定位数据在 HTML 中的位置
要提取数据,需先找到数据对应的 HTML 标签。以 “电影名称” 和 “评分” 为例:
- 打开豆瓣 Top250 第 1 页,右键点击 “查看网页源代码”(或按 F12 打开开发者工具)。
- 按
Ctrl+F
搜索关键词(如 “肖申克的救赎”),找到对应 HTML 结构:
html
<div class="item"> <!-- 每部电影的信息都包裹在class为"item"的div中 --><div class="info"><div class="hd"><a href="https://movie.douban.com/subject/1292052/" class=""><span class="title">肖申克的救赎</span> <!-- 中文电影名 --><span class="title"> / The Shawshank Redemption</span> <!-- 外文片名(可选) --></a></div><div class="bd"><div class="star"><span class="rating_num" property="v:average">9.7</span> <!-- 评分 --><span property="v:best" content="10.0"></span><span class="japanese" property="v:votes">2754844</span> <!-- 评价人数 --></div><p class="">导演: 弗兰克·德拉邦特 Frank Darabont 主演: 蒂姆·罗宾斯 Tim Robbins... <!-- 导演+主演 --><br>1994 / 美国 / 犯罪 剧情 <!-- 上映时间+地区+类型 --></p><p class="quote"><span class="inq">希望是件好东西,也许是世上最好的东西,好东西是永远不会消逝的。</span> <!-- 剧情简介 --></p></div></div>
</div>
规律很清晰:每部电影的信息都在class="item"
的 div 内,我们只需定位到这个 div,再逐层提取子标签中的数据即可。
三、核心代码实现:分三步走
爬虫的核心流程可分为 “请求网页→解析数据→存储数据”,我们按步骤编写代码。
1. 第一步:发送请求,获取网页源代码
用requests.get()
发送 HTTP 请求,注意需要设置headers
模拟浏览器(否则豆瓣会拒绝爬虫请求)。
代码片段:获取单页源代码
python
import requests
from bs4 import BeautifulSoup
import csv # 用于存储数据到CSV文件
import time # 用于设置请求间隔,避免被封IPdef get_page_html(url):"""功能:根据URL发送请求,返回网页的HTML源代码参数:url - 目标网页的URL返回:html_text - 网页源代码(字符串)"""# 设置headers:模拟浏览器请求(替换成你自己的User-Agent)headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"}try:# 发送GET请求response = requests.get(url, headers=headers)# 检查请求是否成功(状态码200表示成功)if response.status_code == 200:# 设置编码为UTF-8,避免中文乱码response.encoding = "utf-8"return response.textelse:print(f"请求失败,状态码:{response.status_code}")return Noneexcept Exception as e:# 捕获请求过程中的异常(如网络错误)print(f"请求出错:{e}")return None
关键说明:
User-Agent
:告诉豆瓣服务器 “我是浏览器,不是爬虫”。你可以在浏览器开发者工具的 “Network”→任意请求→“Request Headers” 中找到自己的User-Agent
,直接复制替换。- 异常处理:用
try-except
捕获网络错误,用status_code
判断请求是否成功,避免程序崩溃。
2. 第二步:解析 HTML,提取目标数据
用BeautifulSoup
解析获取到的 HTML 代码,按之前分析的标签结构提取数据。
代码片段:解析单页数据
python
def parse_page_html(html_text):"""功能:解析HTML源代码,提取单页电影数据参数:html_text - 网页源代码返回:movie_list - 单页电影数据列表(每个元素是字典)"""# 初始化BeautifulSoup对象,指定解析器为lxml(需安装:pip install lxml)soup = BeautifulSoup(html_text, "lxml")# 定位所有电影项(class="item"的div)item_list = soup.find_all("div", class_="item")movie_list = []for item in item_list:# 1. 提取电影名称(中文)title_tag = item.find("span", class_="title") # 第一个title标签是中文名称movie_title = title_tag.get_text() if title_tag else "未知名称"# 2. 提取评分rating_tag = item.find("span", class_="rating_num")movie_rating = rating_tag.get_text() if rating_tag else "0.0"# 3. 提取评价人数votes_tag = item.find("span", property="v:votes")movie_votes = votes_tag.get_text() if votes_tag else "0"# 4. 提取导演与主演(需处理字符串格式)info_tag = item.find("div", class_="bd").find("p", class_="")info_text = info_tag.get_text(strip=True) if info_tag else "" # strip()去除多余空格# 分割导演和主演(格式:"导演: XXX 主演: XXX")if "导演:" in info_text:director_part = info_text.split("主演:")[0].replace("导演:", "").strip()actor_part = info_text.split("主演:")[-1].strip() if "主演:" in info_text else "未知"else:director_part = "未知导演"actor_part = "未知主演"movie_director = director_partmovie_actor = actor_part# 5. 提取上映时间、地区、类型(从info_text的第二行提取)info_lines = info_text.split("\n") # 按换行符分割if len(info_lines) >= 2:year_area_genre = info_lines[1].strip() # 第二行格式:"1994 / 美国 / 犯罪 剧情"year = year_area_genre.split("/")[0].strip() if "/" in year_area_genre else "未知年份"area = year_area_genre.split("/")[1].strip() if len(year_area_genre.split("/")) >=2 else "未知地区"else:year = "未知年份"area = "未知地区"movie_year = yearmovie_area = area# 6. 提取剧情简介quote_tag = item.find("span", class_="inq")movie_quote = quote_tag.get_text() if quote_tag else "无简介"# 整理单部电影数据为字典movie_dict = {"电影名称": movie_title,"评分": movie_rating,"评价人数": movie_votes,"导演": movie_director,"主演": movie_actor,"上映年份": movie_year,"地区": movie_area,"剧情简介": movie_quote}movie_list.append(movie_dict)return movie_list
关键说明:
soup.find()
:查找第一个匹配的标签;soup.find_all()
:查找所有匹配的标签。get_text()
:提取标签内的文本内容;strip()
:去除文本前后的空格、换行符。- 数据容错:用
if...else
判断标签是否存在,避免因部分电影信息缺失导致程序报错(如某些电影没有简介)。
3. 第三步:存储数据,写入 CSV 文件
将提取的所有电影数据写入 CSV 文件,方便后续查看和分析(比纯文本更结构化)。
代码片段:存储数据与主函数
python
def save_to_csv(movie_all, filename):"""功能:将所有电影数据写入CSV文件参数:movie_all - 所有电影数据列表;filename - 输出文件名"""# 定义CSV文件的表头(与movie_dict的键对应)headers = ["电影名称", "评分", "评价人数", "导演", "主演", "上映年份", "地区", "剧情简介"]# 打开文件,使用utf-8-sig编码避免中文乱码,newline=''避免空行with open(filename, "w", encoding="utf-8-sig", newline="") as f:writer = csv.DictWriter(f, fieldnames=headers)writer.writeheader() # 写入表头writer.writerows(movie_all) # 写入所有数据print(f"数据已成功保存到 {filename}")def main():"""主函数:统筹整个爬虫流程(分页请求→解析→汇总数据→保存)"""movie_all = [] # 存储所有页面的电影数据base_url = "https://movie.douban.com/top250?start={}&filter=" # 基础URL,start参数动态替换# 循环爬取10页(start从0到225,步长25)for start in range(0, 250, 25):print(f"正在爬取第 {start//25 + 1} 页...")url = base_url.format(start) # 生成当前页的URLhtml_text = get_page_html(url) # 获取当前页HTMLif html_text:page_movies = parse_page_html(html_text) # 解析当前页数据movie_all.extend(page_movies) # 将当前页数据加入总列表# 设置1秒间隔,避免请求过于频繁被封IPtime.sleep(1)# 保存所有数据到CSV文件save_to_csv(movie_all, "豆瓣电影Top250.csv")print(f"爬虫结束,共抓取到 {len(movie_all)} 部电影数据")# 程序入口:当脚本直接运行时,执行main()函数
if __name__ == "__main__":main()
四、运行程序与查看结果
- 将上述三段代码合并为一个 Python 文件(如
douban_top250_spider.py
)。 - 替换代码中的
User-Agent
为你自己的(否则可能请求失败)。 - 运行脚本:在终端输入
python douban_top250_spider.py
。
预期结果:
- 终端会输出爬取进度:
正在爬取第 1 页...
→正在爬取第 10 页...
- 脚本所在目录会生成
豆瓣电影Top250.csv
文件,用 Excel 或 WPS 打开后,数据如下(示例):
电影名称 | 评分 | 评价人数 | 导演 | 主演 | 上映年份 | 地区 | 剧情简介 |
---|---|---|---|---|---|---|---|
肖申克的救赎 | 9.7 | 2754844 | 弗兰克・德拉邦特 | 蒂姆・罗宾斯 摩根・弗里曼 | 1994 | 美国 | 希望是件好东西,也许是世上最好的东西... |
霸王别姬 | 9.6 | 2025807 | 陈凯歌 | 张国荣 张丰毅 巩俐 | 1993 | 中国大陆 | 说的是一辈子!差一年,一个月,一天... |
五、爬虫伦理与注意事项
作为新手,必须遵守爬虫伦理,避免给目标网站造成负担或触犯法律:
- 设置请求间隔:代码中
time.sleep(1)
表示每爬一页停 1 秒,避免高频请求导致 IP 被封。 - 遵守 Robots 协议:访问https://movie.douban.com/robots.txt ,确认豆瓣是否允许爬取 Top250(目前豆瓣允许非商业用途的温和爬取)。
- 数据用途:仅用于个人学习,不得将爬取的数据用于商业用途或公开传播。
- 避免过度爬取:本项目仅爬取 250 条数据,规模小,不会对豆瓣服务器造成压力。
六、总结与拓展
通过这个项目,你已经掌握了 Python 爬虫的核心流程:
- 用
Requests
发送 HTTP 请求,处理异常; - 用
BeautifulSoup
解析 HTML,定位和提取数据; - 用
CSV
存储结构化数据。
如果想进一步提升,可以尝试这些拓展方向:
- 数据可视化:用
matplotlib
或seaborn
绘制评分分布、地区电影数量柱状图。 - 多线程爬取:用
threading
或concurrent.futures
加快爬取速度(注意控制线程数,避免给服务器造成压力)。 - 存储到数据库:将数据写入
MySQL
或MongoDB
,学习数据库操作。
希望这篇教程能帮你顺利写出第一个爬虫程序,开启 Python 数据采集的大门!