前言:写给零基础的你
本文是纯零基础友好的Python爬虫入门教程,全程手把手、一步步教学,无任何爬虫基础也能看懂、复制就能运行,不会讲复杂的理论,所有知识点都结合实战代码讲解。
我们本次的实战目标:爬取豆瓣电影TOP250的全部电影信息,包括:电影排名、电影名称、豆瓣评分、评分人数、电影简介、导演/主演、上映年份/国家/类型,然后对爬取的原始数据做专业的数据清洗,最后将清洗后的规整数据保存为CSV文件(Excel可直接打开)。
✅ 学习收获
- 掌握Python爬虫的三大核心流程:发送请求 → 解析数据 → 保存数据;
- 学会应对网站反爬机制(豆瓣必做,否则爬取失败);
- 掌握爬虫必备的数据清洗技巧,解决数据乱码、空格、换行、缺失值等问题;
- 学会将爬取的数据保存为CSV文件,满足数据分析/查看需求;
- 收获一套可复用的爬虫模板,以后爬取其他榜单/网页可直接修改使用。
✅ 环境要求(极简,零基础轻松配置)
- Python版本:Python3.7及以上(Python3.x都可以,推荐3.9/3.10)
- 所需依赖库:2个第三方库 + Python内置库,无复杂依赖
requests:用来给豆瓣网站发送请求,获取网页内容(核心爬虫库)lxml:用来解析网页内容,精准提取我们需要的电影数据(解析效率极高,零基础友好)- 内置库:
csv(保存CSV文件)、time(防反爬延时)、re(数据清洗正则),无需安装
一、第一步:环境配置(一行命令安装依赖,零失败)
打开你的Python编辑器(PyCharm、VSCode、IDLE都可以),打开终端/命令行,输入以下命令,一键安装所有需要的依赖库,等待安装完成即可,零基础不用管其他配置:
pipinstallrequests lxml✅ 安装失败解决:如果提示
pip不是内部命令,说明Python未配置环境变量;如果安装慢,可使用国内镜像源:pip install requests lxml -i https://pypi.tuna.tsinghua.edu.cn/simple
二、爬虫前置必学:3个核心知识点(零基础必看,5分钟看懂)
2.1 豆瓣电影TOP250 页面规律(爬取分页的核心)
我们先打开豆瓣电影TOP250的网页:https://movie.douban.com/top250
- 第一页URL:
https://movie.douban.com/top250?start=0→ 展示第1-25条电影 - 第二页URL:
https://movie.douban.com/top250?start=25→ 展示第26-50条电影 - 第三页URL:
https://movie.douban.com/top250?start=50→ 展示第51-75条电影 - …以此类推
核心规律:URL中start=后面的数字,每次增加25,从0到225,一共10页,刚好爬取250条电影数据。这个规律是我们实现「自动爬取多页」的关键!
2.2 爬虫的三大核心流程(所有爬虫通用,记牢即可)
发送请求 → 获取网页内容 → 解析提取数据 → 数据清洗 → 保存数据- 发送请求:用
requests库模拟浏览器访问豆瓣网页,获取网页的HTML源代码; - 解析数据:用
lxml库的xpath语法,从HTML源代码中精准提取我们需要的电影信息(比如名称、评分); - 数据清洗:爬取的原始数据会有多余的空格、换行、无关字符,需要清理成规整的格式;
- 保存数据:将清洗后的干净数据写入CSV文件,方便查看和后续分析。
2.3 豆瓣的反爬机制 & 应对方案(重中之重,必做!)
豆瓣作为正规网站,会限制「非浏览器的恶意请求」,如果我们直接用代码爬取,会被豆瓣拒绝访问,返回403错误,爬取失败。
✅豆瓣的基础反爬:校验请求的「请求头(headers)」,判断是否是浏览器访问。
✅应对方案:给我们的爬虫请求添加「浏览器请求头」,核心只需要加一个User-Agent即可,模拟浏览器访问,豆瓣就会正常返回数据。
补充:User-Agent获取方式(不用记):任意浏览器F12 → 网络 → 随便点一个请求 → 响应头里就能找到,本次教程已经帮你准备好,直接复制用就行。
三、第二步:手把手编写代码(分模块讲解,零基础能跟上)
我们的代码会分模块循序渐进编写:先写「单页爬取」代码,学会提取单页25条数据;再扩展为「多页爬取」,自动爬取10页250条数据;最后加入「数据清洗」和「保存CSV」,全套流程一步到位。
所有代码都有超详细注释,零基础能看懂每一行代码的作用,可以直接复制运行!
3.1 导入所有需要的库
# 发送网络请求的库importrequests# 解析网页的库,核心用xpath语法fromlxmlimportetree# 内置库:保存CSV文件importcsv# 内置库:设置延时,防反爬,避免请求太快被封importtime# 内置库:正则表达式,用于数据清洗importre3.2 配置核心常量(请求头+分页规律+存储列表)
# 1. 请求头:豆瓣必加!模拟浏览器访问,否则爬取失败HEADERS={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"}# 2. 豆瓣TOP250的基础URL,后面拼接start参数实现分页BASE_URL="https://movie.douban.com/top250?start={}"# 3. 创建一个空列表,用来存储爬取的所有电影数据(250条)movie_list=[]3.3 编写【数据爬取+数据清洗】核心函数
这是整个爬虫的核心,我们定义一个函数,传入start参数,实现「爬取指定页面+提取数据+清洗数据」的功能,所有数据清洗的逻辑都在这里实现,爬取的原始数据会被清洗得干干净净!
爬取的原始数据问题(为什么要清洗)
- 电影简介有大量换行、空格、多余的缩进;
- 导演/主演信息有多余的空格和无关字符;
- 上映年份/国家/类型是混在一起的字符串,需要拆分;
- 评分人数有「人评价」的文字,需要提取纯数字;
- 部分电影的简介为空,需要填充默认值;
所有这些问题,我们都在函数里做专业清洗,保证最终数据的规整性。
defget_movie_info(start):""" 爬取指定分页的电影数据,并完成数据清洗 :param start: 分页参数,0,25,50...225 """# 1. 构造当前页的URLurl=BASE_URL.format(start)# 2. 发送请求,获取网页响应response=requests.get(url=url,headers=HEADERS)# 3. 将网页响应的文本内容,转为可解析的xpath对象html=etree.HTML(response.text)# 4. 定位到页面中所有的电影条目,每页25个movie_items=html.xpath("//ol[@class='grid_view']/li")# 5. 遍历每一个电影条目,提取数据foriteminmovie_items:# ===== 提取原始数据 =====rank=item.xpath(".//em[@class='']/text()")[0]# 电影排名movie_name=item.xpath(".//span[@class='title'][1]/text()")[0]# 电影名称(主名)score=item.xpath(".//span[@class='rating_num']/text()")[0]# 豆瓣评分score_num=item.xpath(".//div[@class='star']/span[4]/text()")[0]# 评分人数quote=item.xpath(".//span[@class='inq']/text()")# 电影简介/名言(可能为空)info=item.xpath(".//p[@class='']")[0].xpath("string(.)")# 导演/主演/年份/国家/类型# ===== 数据清洗:核心步骤,解决所有数据格式问题 =====# 清洗1:电影简介,为空则填充「暂无简介」,有则去除空格和换行movie_quote=quote[0].strip()ifquoteelse"暂无简介"# 清洗2:评分人数,提取纯数字(如:123456人评价 → 123456)clean_score_num=re.findall(r"\d+",score_num)[0]# 清洗3:导演/主演 与 年份/国家/类型 拆分,去除多余空格和换行info=re.sub(r"\s+"," ",info).strip()# 所有空白符替换为单个空格director_actor=info.split("|")[0].strip()# 导演+主演year_country_type=info.split("|")[1].strip()# 年份+国家+类型# 清洗4:拆分 上映年份、上映国家、电影类型year=re.findall(r"\d{4}",year_country_type)[0]# 提取4位数字的年份country=re.findall(r"[\u4e00-\u9fa5]+|[a-zA-Z]+",year_country_type.split("/")[0].replace(year,"").strip())[0]movie_type=year_country_type.split("/")[-1].strip()# ===== 组装清洗后的干净数据 =====movie_dict={"排名":rank,"电影名称":movie_name,"豆瓣评分":score,"评分人数":clean_score_num,"电影简介":movie_quote,"导演主演":director_actor,"上映年份":year,"上映国家":country,"电影类型":movie_type}# 6. 将清洗后的单条电影数据添加到总列表movie_list.append(movie_dict)print(f"已爬取:排名{rank}→{movie_name}| 评分:{score}")3.4 编写【多页爬取】循环逻辑
利用我们发现的分页规律,从0到225,步长25,循环10次,自动爬取全部10页数据。
重要优化:每次爬取一页后,加time.sleep(1)延时1秒,防反爬关键!避免请求频率过快,被豆瓣封禁IP,零基础一定要加这一步!
# 循环爬取10页数据,start参数:0,25,50,...,225forstartinrange(0,250,25):get_movie_info(start)time.sleep(1)# 每页爬取后延时1秒,防反爬print("="*50)print(f"爬取完成!共爬取{len(movie_list)}条豆瓣电影TOP250数据")3.5 编写【保存CSV文件】核心代码
将清洗后的250条电影数据,保存为CSV文件,Excel可直接打开查看,解决零基础最头疼的「中文乱码问题」!
关键避坑点(必看)
- 保存CSV时,编码必须用
utf-8-sig,而不是utf-8!否则Excel打开会出现中文乱码,这是爬虫保存CSV的高频大坑,本次教程直接帮你避坑; - CSV的表头要和我们组装的字典的key一致,保证数据对应正确;
- 用
newline=''参数,避免CSV文件中出现多余的空行。
defsave_to_csv():"""将爬取的电影数据保存为CSV文件"""# 1. 定义CSV文件的表头,和字典的key一一对应headers=["排名","电影名称","豆瓣评分","评分人数","电影简介","导演主演","上映年份","上映国家","电影类型"]# 2. 打开文件,写入数据withopen("豆瓣电影TOP250_清洗后.csv","w",encoding="utf-8-sig",newline="")asf:# 创建csv写入对象writer=csv.DictWriter(f,fieldnames=headers)# 写入表头writer.writeheader()# 写入所有电影数据writer.writerows(movie_list)print("数据已成功保存为:豆瓣电影TOP250_清洗后.csv")# 调用保存函数save_to_csv()四、完整版可运行代码(一键复制,零修改直接运行)
将上面所有模块整合,得到完整的全套代码,无任何删减,注释完整,零基础直接复制粘贴运行即可,运行后会在当前目录生成CSV文件,全程无需修改任何内容!
importrequestsfromlxmlimportetreeimportcsvimporttimeimportre# 1. 配置请求头、基础URL、存储列表HEADERS={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"}BASE_URL="https://movie.douban.com/top250?start={}"movie_list=[]# 2. 爬取+清洗数据核心函数defget_movie_info(start):url=BASE_URL.format(start)response=requests.get(url=url,headers=HEADERS)html=etree.HTML(response.text)movie_items=html.xpath("//ol[@class='grid_view']/li")foriteminmovie_items:# 提取原始数据rank=item.xpath(".//em[@class='']/text()")[0]movie_name=item.xpath(".//span[@class='title'][1]/text()")[0]score=item.xpath(".//span[@class='rating_num']/text()")[0]score_num=item.xpath(".//div[@class='star']/span[4]/text()")[0]quote=item.xpath(".//span[@class='inq']/text()")info=item.xpath(".//p[@class='']")[0].xpath("string(.)")# 数据清洗movie_quote=quote[0].strip()ifquoteelse"暂无简介"clean_score_num=re.findall(r"\d+",score_num)[0]info=re.sub(r"\s+"," ",info).strip()director_actor=info.split("|")[0].strip()year_country_type=info.split("|")[1].strip()year=re.findall(r"\d{4}",year_country_type)[0]country=re.findall(r"[\u4e00-\u9fa5]+|[a-zA-Z]+",year_country_type.split("/")[0].replace(year,"").strip())[0]movie_type=year_country_type.split("/")[-1].strip()# 组装数据movie_dict={"排名":rank,"电影名称":movie_name,"豆瓣评分":score,"评分人数":clean_score_num,"电影简介":movie_quote,"导演主演":director_actor,"上映年份":year,"上映国家":country,"电影类型":movie_type}movie_list.append(movie_dict)print(f"已爬取:排名{rank}→{movie_name}| 评分:{score}")# 3. 多页爬取循环if__name__=="__main__":print("开始爬取豆瓣电影TOP250数据...")print("="*50)forstartinrange(0,250,25):get_movie_info(start)time.sleep(1)print("="*50)print(f"爬取完成!共爬取{len(movie_list)}条数据")# 4. 保存CSV文件headers=["排名","电影名称","豆瓣评分","评分人数","电影简介","导演主演","上映年份","上映国家","电影类型"]withopen("豆瓣电影TOP250_清洗后.csv","w",encoding="utf-8-sig",newline="")asf:writer=csv.DictWriter(f,fieldnames=headers)writer.writeheader()writer.writerows(movie_list)print("✅ 数据已成功保存为:豆瓣电影TOP250_清洗后.csv")五、运行结果展示(零基础看效果)
5.1 控制台运行日志
运行代码后,控制台会实时打印爬取进度,如下所示,清晰看到每一条电影的爬取状态:
开始爬取豆瓣电影TOP250数据... ================================================== 已爬取:排名1 → 肖申克的救赎 | 评分:9.7 已爬取:排名2 → 霸王别姬 | 评分:9.6 已爬取:排名3 → 阿甘正传 | 评分:9.5 已爬取:排名4 → 泰坦尼克号 | 评分:9.5 ... 已爬取:排名250 → 教父3 | 评分:9.0 ================================================== 爬取完成!共爬取 250 条数据 ✅ 数据已成功保存为:豆瓣电影TOP250_清洗后.csv5.2 CSV文件效果
运行完成后,在你的Python代码同目录下,会生成一个名为豆瓣电影TOP250_清洗后.csv的文件,用Excel/WPS直接打开即可,中文无乱码,数据规整干净,效果如下:
| 排名 | 电影名称 | 豆瓣评分 | 评分人数 | 电影简介 | 导演主演 | 上映年份 | 上映国家 | 电影类型 |
|---|---|---|---|---|---|---|---|---|
| 1 | 肖申克的救赎 | 9.7 | 1986330 | 希望让人自由。 | 导演: 弗兰克·德拉邦特 主演: 蒂姆·罗宾斯 / 摩根·弗里曼 | 1994 | 美国 | 剧情/犯罪 |
| 2 | 霸王别姬 | 9.6 | 1458997 | 风华绝代。 | 导演: 陈凯歌 主演: 张国荣 / 张丰毅 / 巩俐 | 1993 | 中国大陆 | 剧情/音乐/历史 |
| … | … | … | … | … | … | … | … | … |
六、零基础必看:常见问题&避坑指南(爬取失败的99%原因都在这里)
作为零基础,第一次运行爬虫代码很可能遇到各种问题,这里汇总了最常见的8个问题+完美解决方案,按出现频率排序,99%的爬取失败都能在这里解决,建议收藏备查!
❌ 问题1:运行代码提示ModuleNotFoundError: No module named 'requests'
✅ 原因:没有安装对应的依赖库
✅ 解决:重新执行安装命令pip install requests lxml
❌ 问题2:爬取时没有任何数据输出,或者提示403 Forbidden
✅ 原因:没有加请求头headers,被豆瓣的反爬机制拒绝访问
✅ 解决:检查代码中是否有headers参数,并且User-Agent是否正确,直接复制教程里的即可
❌ 问题3:CSV文件打开后中文乱码,全是问号/方块
✅ 原因:保存时编码用了utf-8,Excel不兼容该编码
✅ 解决:必须将编码改为utf-8-sig,教程里的代码已经是这个编码,直接复制即可
❌ 问题4:爬取过程中程序中断,提示IndexError: list index out of range
✅ 原因:请求频率太快,豆瓣返回的网页内容不完整,导致xpath提取不到数据
✅ 解决:在循环中加入time.sleep(1),延长延时时间,比如改为time.sleep(2)
❌ 问题5:部分电影的「上映国家」提取错误
✅ 原因:部分电影的国家名称格式特殊
✅ 解决:不影响整体数据,零基础可以忽略,进阶可以优化正则表达式
❌ 问题6:CSV文件中有很多空行
✅ 原因:保存时没有加newline=''参数
✅ 解决:在open()函数中添加newline='',教程里的代码已添加
❌ 问题7:爬取的评分人数是「123456人评价」,不是纯数字
✅ 原因:忘记加正则清洗,或者清洗逻辑写错
✅ 解决:检查代码中的clean_score_num = re.findall(r"\d+", score_num)[0]是否存在
❌ 问题8:爬取的电影数量不足250条
✅ 原因:豆瓣的分页是10页250条,循环范围写错
✅ 解决:检查循环是否是range(0,250,25),而不是range(0,225,25)
七、零基础进阶拓展(可选,提升爬虫能力)
学会了本次的豆瓣爬虫,你已经掌握了Python爬虫的核心技能,这里给零基础的你提供几个简单的拓展方向,不用修改太多代码,就能实现更多功能,快速提升爬虫能力!
✅ 拓展1:保存数据到Excel文件(.xlsx格式)
如果想保存为Excel的xlsx格式,只需要安装openpyxl库,然后用pandas库保存即可,代码如下(在原有代码基础上添加):
pipinstallpandas openpyxlimportpandasaspd df=pd.DataFrame(movie_list)df.to_excel("豆瓣电影TOP250_清洗后.xlsx",index=False,encoding="utf-8-sig")✅ 拓展2:增加爬取字段(比如电影时长、海报链接)
在get_movie_info函数中,添加对应的xpath提取即可,比如提取海报链接:
img_url=item.xpath(".//img[@width='100']/@src")[0]# 电影海报链接然后在字典中添加"海报链接": img_url即可。
✅ 拓展3:爬取其他榜单(豆瓣读书TOP250、豆瓣音乐TOP250)
只需要修改BASE_URL和xpath提取路径,核心的爬虫流程、反爬、数据清洗逻辑完全不变,这就是爬虫模板的复用性!
八、总结:零基础爬虫的核心收获
本次教程从0到1完成了豆瓣电影TOP250的爬取,你不仅收获了一套完整的爬虫代码,更重要的是掌握了Python爬虫的底层逻辑和通用流程,所有爬虫的核心都是这几步:
分析网页规律 → 配置请求头防反爬 → 发送请求获取数据 → 解析提取数据 → 清洗数据 → 保存数据对于零基础来说,不用一开始就追求复杂的爬虫框架(比如Scrapy),先把基础的requests+lxml吃透,能爬取大部分静态网页,足够应对日常的爬虫需求。
本次爬取的豆瓣电影TOP250数据,还可以做简单的数据分析:比如统计高分电影的上映年份、不同国家的电影数量、评分人数最多的电影等,零基础可以继续探索!
最后,温馨提示:爬虫请遵守网站的robots协议,合理爬取,不要频繁请求,尊重网站的服务器资源。豆瓣的TOP250是公开的榜单,合理爬取用于学习是完全没问题的。
祝你爬虫入门顺利,解锁Python的又一项核心技能!🚀