爬虫阶段一实战练习题:爬取豆瓣电影 Top250
- [爬取豆瓣电影 Top250](#爬取豆瓣电影 Top250)
-
- [练习一:爬取豆瓣电影 Top250](#练习一:爬取豆瓣电影 Top250)
- 一、HTML解析与数据提取
-
- [1. **识别页面结构**](#1. 识别页面结构)
- [2. **使用BeautifulSoup精准提取**](#2. 使用BeautifulSoup精准提取)
- [3. **正则表达式的实战应用**](#3. 正则表达式的实战应用)
- 二、分页处理与URL构造
-
- [1. **豆瓣分页规则**](#1. 豆瓣分页规则)
- [2. **循环构造URL**](#2. 循环构造URL)
- 三、网络请求与反爬策略
-
- [1. **模拟浏览器请求**](#1. 模拟浏览器请求)
- [2. **控制请求频率**](#2. 控制请求频率)
- [3. **异常处理**](#3. 异常处理)
- 四、数据存储与清洗
-
- [1. **存储格式选择**](#1. 存储格式选择)
- [2. **数据清洗**](#2. 数据清洗)
- 五、编码与文件处理
-
- [1. **避免中文乱码**](#1. 避免中文乱码)
- [2. **文件操作**](#2. 文件操作)
- 六、调试与模块化
-
- [1. **分步调试**](#1. 分步调试)
- [2. **函数封装**](#2. 函数封装)
- [3. **使用`if name == 'main':`**](#3. 使用
if __name__ == '__main__':)
- 七、法律与道德
- 八、数据分析的延伸思考
爬取豆瓣电影 Top250
练习一:爬取豆瓣电影 Top250
- 目标:https://movie.douban.com/top250
- 数据字段:电影排名(1-250)、中文片名、导演、上映年份、评分、评价人数
- 要求:分页爬取(共 10 页),保存为 CSV 文件。
- 提示 :
- 第一页 URL:
https://movie.douban.com/top250?start=0,第二页start=25,以此类推。 - 需要设置
User-Agent请求头,否则会返回 418。 - 使用 BeautifulSoup 解析,每个电影信息在一个
<li>标签中,class="item"。 - 电影排名在
<em>标签中。 - 片名在
span标签class="title"中(注意可能有英文名干扰,取第一个)。 - 导演等其他信息在
<p class="">中,需要正则或 split 处理。 - 评分在
span标签class="rating_num"中。 - 评价人数在
<div class="star">内的最后一个<span>中,需提取数字。
- 第一页 URL:
抓取的电影数据示例:
html
[<div class="item">
<div class="pic">
<em>1</em>
<a href="https://movie.douban.com/subject/1292052/">
<img alt="肖申克的救赎" src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg" width="100"/>
</a>
</div>
<div class="info">
<div class="hd">
<a href="https://movie.douban.com/subject/1292052/">
<span class="title">肖申克的救赎</span>
<span class="title"> / The Shawshank Redemption</span>
<span class="other"> / 月黑高飞(港) / 刺激1995(台)</span>
</a>
<span class="playable">[可播放]</span>
</div>
<div class="bd">
<p>
导演: 弗兰克·德拉邦特 Frank Darabont 主演: 蒂姆·罗宾斯 Tim Robbins /...<br/>
1994 / 美国 / 犯罪 剧情
</p>
<div>
<span class="rating5-t"></span>
<span class="rating_num" property="v:average">9.7</span>
<span content="10.0" property="v:best"></span>
<span>3266928人评价</span>
</div>
<p class="quote">
<span>希望让人自由。</span>
</p>
</div>
</div>
</div>, <div class="item">
<div class="pic">
<em>2</em>
<a href="https://movie.douban.com/subject/1291546/">
<img alt="霸王别姬" src="https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2911205318.jpg" width="100"/>
</a>
</div>
<div class="info">
<div class="hd">
<a href="https://movie.douban.com/subject/1291546/">
<span class="title">霸王别姬</span>
<span class="other"> / 再见,我的妾 / Farewell My Concubine</span>
</a>
<span class="playable">[可播放]</span>
</div>
<div class="bd">
<p>
导演: 陈凯歌 Kaige Chen 主演: 张国荣 Leslie Cheung / 张丰毅 Fengyi Zha...<br/>
1993 / 中国大陆 中国香港 / 剧情 爱情 同性
</p>
<div>
<span class="rating5-t"></span>
<span class="rating_num" property="v:average">9.6</span>
<span content="10.0" property="v:best"></span>
<span>2409980人评价</span>
</div>
<p class="quote">
<span>风华绝代。</span>
</p>
</div>
</div>
</div>,...]
调试后的爬虫代码
python
import requests
from bs4 import BeautifulSoup
import re
import csv
import time
def fetch_page(start):
url = f'https://movie.douban.com/top250?start={start}'
headers = {'User-Agent': 'Mozilla/5.0'}
#请求url
resp = requests.get(url, headers=headers)
#它的作用是把 HTML 字符串真正解析成 Python 对象。
soup = BeautifulSoup(resp.text, 'lxml')
movies = soup.select('.item')
page_data = []
for movie in movies:
rank = movie.find('em').text
title = movie.find('span', class_='title').text
# 提取导演、年份等
#strip()处理两端的字符
infobd = movie.find('div', class_='bd')
info = infobd.find('p').text.strip()
#info2= "导演: 文牧野 Muye Wen 主演: 徐峥 Zheng Xu / 王传君 Chuanjun Wang / 周...<br/>2018 / 中国大陆 / 剧情 喜剧"
# 用正则或字符串分割提取导演和年份
director_info = re.search(r'导演:(.*?)主演', info)
if director_info:
director = director_info.group(1).strip().replace('\xa0', '')
year = re.search(r'\b(19|20)\d{2}\b', info).group()
# 提取评分
rating = movie.find('span', class_='rating_num').text
# 提取评价人数
people_span = movie.find ('span',string=re.compile(r'\d+人评价'))
if people_span:
people_text = people_span.get_text(strip=True)
num = re.search(r'\d+', people_text).group()
page_data.append([rank, title, director, year, rating, num])
return page_data
all_data = [['排名', '片名', '导演', '年份', '评分', '评价人数']]
for i in range(0, 250, 25):
all_data.extend(fetch_page(i))
time.sleep(2) # 礼貌延时
with open('douban_top250.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.writer(f)
writer.writerows(all_data)
通过爬取豆瓣TOP250电影数据,你一定积累了不少宝贵的经验和技巧。下面从多个维度帮你梳理一下可能学到的内容,既是对知识的复盘,也能为后续项目提供参考。
一、HTML解析与数据提取
1. 识别页面结构
豆瓣TOP250每个电影条目都包裹在<div class="item">中,内部又分为pic(海报、排名)和info(详情)。学会用浏览器开发者工具(F12)快速定位元素,是爬虫的基本功。
2. 使用BeautifulSoup精准提取
- 标题 :从
<span class="title">获取中文名,注意可能有第二外语名(用<span class="title">的第二个或<span class="other">)。 - 评分 :
<span class="rating_num" property="v:average">直接取出数字。 - 评价人数 :位于评分父节点内的最后一个
<span>,文本如"3266928人评价"。通过.get_text()提取后用正则\d+抓取数字。 - 引用语 :
<p class="quote"><span>,若存在则直接取出。 - 导演/主演/年份/地区/类型 :这些信息混在
<p>标签的一段文本中,需要结合正则进一步提取。
3. 正则表达式的实战应用
- 从混合文本中提取导演:
r'导演:\s*(.*?)\s*主演',理解非贪婪匹配.*?和捕获组(...),以及\s*匹配空白。 - 提取年份:
r'\b(19|20)\d{2}\b'或根据豆瓣格式r'(\d{4})\s*/\s*'。 - 注意特殊字符如不间断空格
\xa0,通过.replace('\xa0', ' ')清洗。 - match对象的方法 :
.group(0)返回整个匹配,.group(1)返回第一个捕获组,.groups()返回所有捕获组元组。
二、分页处理与URL构造
1. 豆瓣分页规则
每页显示25条,通过start参数偏移:
- 第1页:
start=0 - 第2页:
start=25 - ... 第10页:
start=225
2. 循环构造URL
python
base_url = "https://movie.douban.com/top250"
for start in range(0, 250, 25):
url = f"{base_url}?start={start}&filter="
# 请求并解析
这里range(0,250,25)生成10个页码,完美覆盖所有条目。
三、网络请求与反爬策略
1. 模拟浏览器请求
豆瓣对爬虫较敏感,必须添加请求头(尤其是User-Agent),否则可能返回403或重定向到登录页。
python
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...'
}
requests.get(url, headers=headers)
2. 控制请求频率
在循环中适当time.sleep(1),避免请求过快导致IP被封。还可以使用requests.Session()保持连接,复用TCP。
3. 异常处理
网络波动、解析失败都可能发生,用try...except包裹关键代码,并打印错误信息,确保爬虫不会中途崩溃。
四、数据存储与清洗
1. 存储格式选择
- CSV :标准表格格式,适合Excel打开,使用
csv模块写入。 - JSON:便于后续程序读取,结构清晰。
- Excel :可用
pandas或openpyxl直接生成。
2. 数据清洗
- 去除字符串首尾空格:
.strip() - 替换特殊空白:
.replace('\xa0', ' ') - 数字类型转换:评价人数转为
int,评分转为float,便于后续分析。
五、编码与文件处理
1. 避免中文乱码
- 请求时,
response.encoding可设为'utf-8'(豆瓣返回的编码通常是utf-8)。 - 写入CSV时,指定
encoding='utf-8-sig',防止Excel打开乱码。
2. 文件操作
使用with open(...) as f确保文件正确关闭,是良好的编程习惯。
六、调试与模块化
1. 分步调试
每解析一个字段,就print一下结果,确保提取正确后再继续。可以用Jupyter Notebook交互式调试。
2. 函数封装
将获取页面、解析单页、保存数据等功能拆分为独立函数,提高代码可读性和复用性。例如:
python
def get_page(url):
# 请求并返回soup
def parse_item(item):
# 从单个item提取数据,返回字典
def save_to_csv(data_list):
# 写入文件
3. 使用if __name__ == '__main__':
防止模块被导入时执行爬虫代码。
七、法律与道德
- 遵守robots.txt :豆瓣的
robots.txt禁止了某些路径,但TOP250通常是允许的。 - 适度爬取:不要对服务器造成压力,添加延时。
- 数据仅用于学习:避免商用或大量分发,尊重版权。
八、数据分析的延伸思考
爬下来的数据还可以进一步探索:
- 评分分布:哪些分数段电影最多?
- 年份趋势:哪个年代出产的高分电影最多?
- 导演/演员频次:谁的作品上榜最多?
- 类型分析:剧情、犯罪、爱情等类型的占比。
可以结合pandas、matplotlib做可视化,让爬虫成果更有趣。
通过这次实战,你不仅掌握了爬虫的基本流程(请求→解析→存储),还深入了解了HTML结构、正则表达式、反爬策略等实用技能。这些知识完全可以迁移到其他网站的爬取中,是成为数据工程师/分析师的重要一步。如果在总结中还有不清楚的地方,或者想深入探讨某个细节,随时欢迎继续交流!