
之前后台经常看到有人私信问Python 学了基础语法之后接下来该练点什么我的建议基本都是同一个写一个网络爬虫。原因很简单爬虫能把 Python 的基础语法、数据处理、文件读写、异常处理全部串起来而且写完立刻能看到结果那种“用代码改变世界”的成就感特别直接。本文就以一套完整的实战流程为主线带零基础的同学从环境搭建开始一路写到能爬取真实网站数据并保存到本地。无论你是刚学完 Python 基本语法的新手还是想系统梳理爬虫知识体系的进阶开发者这篇文章都值得收藏备用。文章会先解释爬虫的核心概念与法律边界再带你准备环境、理解 HTTP 基础最后用一个完整的豆瓣电影 Top250 爬虫案例把知识点串起来并附上高频报错排查清单与工程建议。1. 爬虫是什么先搞清楚这几个核心概念1.1 网络爬虫的定义网络爬虫Web Crawler / Spider是一种按照一定规则自动请求互联网上的网页并提取所需数据的程序。你可以把它想象成一个“机器人版”的浏览器你在浏览器里看到一篇好文章会复制粘贴保存爬虫则是自动完成“打开网页、提取信息、保存数据”这一整套动作的脚本。爬虫的工作流程本质上就是四步构造请求向目标网址发送 HTTP 请求。获取响应接收服务器返回的 HTML、JSON 或二进制数据。解析数据从响应内容中提取我们关心的字段。存储数据把提取结果保存为 CSV、Excel、数据库或 JSON 文件。1.2 爬虫的典型应用场景爬虫的用途远比“爬电影榜单”广泛在实际开发中常见的场景包括舆情监控定时抓取新闻、社交平台关键词分析舆论走向。电商比价监控竞品价格变动辅助定价决策。数据采集为数据分析、机器学习模型采集训练数据。内容聚合把分散在多个网站的信息聚合到一个平台。自动化测试模拟用户请求验证接口数据完整性。即使你以后不做专业的爬虫工程师掌握爬虫也会让你在处理数据获取类需求时比同事快一步。很多后端开发、测试开发、数据分析师的岗位要求里都写着“熟悉爬虫者优先”。1.3 爬虫的合法边界与道德约束必看在学习之前必须先强调一个原则爬虫是一把工具工具本身没有善恶但使用方式必须守住边界。根据常见司法实践和行业惯例写爬虫时需要遵守以下几条底线遵守 robots 协议大部分网站都会在robots.txt中声明允许或禁止爬取的路径例如豆瓣的https://www.douban.com/robots.txt。虽然 robots 协议不是法律但遵守它是行业基本礼仪。控制请求频率不要用高并发去冲击目标服务器这会给对方带来负载压力严重的可能构成破坏计算机信息系统。不爬取个人隐私数据不得采集姓名、电话、地址、身份证号等个人信息。不用于商业竞争和牟利未经授权大规模爬取并商用法律风险极高。本文案例仅用于学习文中使用的豆瓣案例仅为教学演示请控制频率禁止用于任何商业用途。记住一句话爬虫爬的是“公开数据”的“合理使用”而不是把对方网站整个搬走。2. 环境准备与版本说明2.1 安装 Python目前 Python 已发布到 3.13 系列但考虑到第三方库兼容性建议使用 3.9 至 3.12 版本本文示例以 Python 3.10 为主。如果你使用的是 3.8 或更早版本建议升级因为部分库的新版 API 已不再兼容旧版。Windows 用户请前往 Python 官网下载安装包安装时务必勾选“Add Python to PATH”否则命令行里无法直接使用python命令。注意版本Python 版本需要根据你的项目实际情况调整本文示例以常见稳定版本为例重点演示配置与编码思路。安装完成后打开命令行Windows 用 CMD 或 PowerShellmacOS / Linux 用终端输入以下命令验证python --version如果输出类似Python 3.10.x说明安装成功。2.2 选择 IDE 与运行方式对于新手我推荐两种方式PyCharm Community Edition免费功能强大适合写较完整的爬虫项目。VS Code Python 插件轻量插件丰富配置好后体验也很好。如果你只是临时跑个脚本直接使用命令行写.py文件并运行也完全可以。本文的案例代码是完整脚本任何 IDE 或命令行都能运行。2.3 安装核心第三方库爬虫开发最常用的库包括requests发 HTTP 请求是爬虫的“手”。BeautifulSoup4解析 HTML是爬虫的“眼睛”。lxmlBeautifulSoup 背后可选的解析引擎速度更快。使用 pip 安装pip install requests beautifulsoup4 lxml如果 pip 下载速度慢可以临时使用清华镜像源pip install requests beautifulsoup4 lxml -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以写一个简单的导入测试import requests import bs4 print(requests 版本:, requests.__version__) print(BeautifulSoup4 导入成功)2.4 项目结构规划为了方便后续扩展假设你在本地新建一个文件夹spider_demo结构如下spider_demo/ ├── main.py # 爬虫主程序 ├── requirements.txt # 依赖清单 └── output/ # 保存抓取结果的文件夹后面实战部分的代码就放在main.py中抓取结果输出到output/目录。3. 爬虫核心基础HTTP 与 Requests 库3.1 HTTP 请求与响应术语在写爬虫代码之前必须理解 HTTP 的几个基本概念URL统一资源定位符也就是网址例如https://www.example.com/path?query1。Method请求方法最常见的是 GET获取数据和 POST提交数据。Header请求头包含浏览器标识、Cookie、Referer 等信息。服务器会通过 Header 判断请求是否来自真实浏览器。Body请求体POST 请求时携带的表单数据或 JSON 数据。Status Code状态码例如 200 表示成功403 表示禁止访问404 表示页面不存在500 表示服务器错误。其中User-Agent是最容易踩坑的一项。很多网站会拒绝没有User-Agent或 User-Agent 明显不是浏览器的请求所以写爬虫时一般都会主动设置它。3.2 requests 库最常用的五个方法requests库的 API 设计非常简洁下面是五个最常用的方法。GET 请求import requests url https://httpbin.org/get response requests.get(url) print(response.status_code) # 200 print(response.text) # 响应内容字符串带参数的 GET 请求有些网址的参数在问号后面例如https://httpbin.org/get?namepythonpage2。推荐使用params参数让 requests 自动拼接import requests url https://httpbin.org/get params { name: python, page: 2 } response requests.get(url, paramsparams) print(response.url)自定义请求头import requests url https://httpbin.org/get headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } response requests.get(url, headersheaders) print(response.status_code)设置超时时间网络请求可能因为对方服务器慢、网络波动等原因卡住为了避免程序一直等待必须设置超时时间import requests try: response requests.get(https://httpbin.org/get, timeout10) print(response.status_code) except requests.Timeout: print(请求超时)获取 JSON 数据很多网站接口返回的是 JSON 而非 HTML。requests 提供了一个很方便的.json()方法import requests url https://httpbin.org/json response requests.get(url) data response.json() print(data[slideshow][title])3.3 用 BeautifulSoup 解析 HTML拿到 HTML 之后直接用字符串截取的方式提取数据会非常痛苦BeautifulSoup 的作用就是把 HTML 转换成可操作的树形结构。先看一个最小示例from bs4 import BeautifulSoup html htmlbodyh1你好爬虫/h1p classdesc这是一段描述/p/body/html soup BeautifulSoup(html, html.parser) print(soup.h1.text) # 你好爬虫 print(soup.find(p).text) # 这是一段描述 print(soup.find(p, class_desc).text) # 这是一段描述这里需要注意三点class_后面有一个下划线因为class是 Python 关键字。.text获取标签内部的文本内容。html.parser是 Python 自带的解析器lxml 解析速度更快二选一即可。3.4 用 CSS 选择器定位元素BeautifulSoup 的.select()方法支持 CSS 选择器这是我在实战中最常用的定位方式。例如from bs4 import BeautifulSoup html div classmovie-list div classitem span classtitle电影A/span span classrating9.0/span /div div classitem span classtitle电影B/span span classrating8.5/span /div /div soup BeautifulSoup(html, html.parser) # 选择所有 class 为 item 的 div items soup.select(.movie-list .item) for item in items: title item.select(.title)[0].text rating item.select(.rating)[0].text print(title, rating)CSS 选择器的常见写法写法含义.titleclass 为 title 的元素#idid 为该值的元素div pdiv 内部的所有 p 后代元素div pdiv 下的直接子元素 pa[href]包含 href 属性的 a 标签这个语法后面实战会反复用到建议先存下来。4. 完整实战爬取豆瓣电影 Top2504.1 需求分析与页面结构分析先明确目标抓取豆瓣电影 Top250 榜单的排名、电影名、评分和简介并把结果保存成 CSV 文件。豆瓣电影 Top250 地址为https://movie.douban.com/top250?start0filter这个榜单一共 10 页每页 25 条URL 中的start参数每页加 25分别是 0、25、50……225。打开页面后按 F12 进入开发者工具选中“查看器/元素”面板可以定位到每一条电影信息所在的 DOM 结构。关键选择器如下列表容器ol.grid_view每一条电影ol.grid_view li电影标题.hd .title评分.rating_num简介与主演.bd p评分人数.star span的最后一个 span选好定位点之后代码写起来就非常顺畅。4.2 编写抓取脚本新建main.py输入以下完整代码# -*- coding: utf-8 -*- 豆瓣电影 Top250 爬虫示例 仅用于学习交流请控制请求频率勿用于商业用途 import csv import time import requests from bs4 import BeautifulSoup # 构造请求头模拟真实浏览器 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, Accept-Language: zh-CN,zh;q0.9, } BASE_URL https://movie.douban.com/top250 def fetch_page(start): 抓取指定页的数据start 为起始偏移量 params { start: start, filter: } try: response requests.get(BASE_URL, paramsparams, headersHEADERS, timeout10) response.encoding utf-8 response.raise_for_status() # 如果状态码不是 200会抛出异常 return response.text except requests.RequestException as e: print(f请求失败: start{start}, 错误信息: {e}) return None def parse_page(html): 解析 HTML提取电影名、评分、简介和排名 soup BeautifulSoup(html, html.parser) items soup.select(ol.grid_view li) movies [] for item in items: rank item.select(.pic em)[0].text title item.select(.hd .title)[0].text.strip() rating item.select(.rating_num)[0].text.strip() # bd 里的 p 标签包含导演、主演、年份等信息这里做简单清洗 info item.select(.bd p)[0].text.strip().replace(\n, ).replace( , ) movies.append([rank, title, rating, info]) return movies def save_to_csv(movies, filenameoutput/douban_top250.csv): 保存到 CSV 文件使用 utf-8-sig 编码避免 Excel 中文乱码 with open(filename, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([排名, 电影名, 评分, 简介]) writer.writerows(movies) def main(): all_movies [] for start in range(0, 250, 25): print(f正在抓取 start{start} 的数据...) html fetch_page(start) if html: movies parse_page(html) all_movies.extend(movies) print(f - 成功解析 {len(movies)} 条数据) # 控制请求频率防止给目标网站造成压力 time.sleep(2) if all_movies: save_to_csv(all_movies) print(f全部完成共抓取 {len(all_movies)} 条数据已保存到 output/douban_top250.csv) else: print(没有抓取到任何数据请检查网络或页面结构是否发生变化) if __name__ __main__: main()4.3 代码结构说明这段代码分成四个函数职责非常清楚fetch_page(start)负责发请求返回 HTML 字符串。失败了返回None不会中断整个程序。parse_page(html)负责解析单个页面的 HTML返回当前页的电影列表。save_to_csv(movies)负责把数据写入 CSV 文件。main()负责控制整个流程循环翻页、调用子函数、控制频率。这样拆分的好处是以后换目标网站时只需要改parse_page里的选择器其他部分基本可以复用。这也是工程化爬虫的基本思路。4.4 运行与验证在项目目录下先创建output文件夹然后运行mkdir output python main.py如果你是在 IDE 里运行直接点击运行按钮即可。正常输出如下正在抓取 start0 的数据... - 成功解析 25 条数据 正在抓取 start25 的数据... - 成功解析 25 条数据 ... 全部完成共抓取 250 条数据已保存到 output/douban_top250.csv用 Excel 或记事本打开output/douban_top250.csv可以看到类似下面的内容排名电影名评分简介1肖申克的救赎9.7导演: 弗兰克·德拉邦特 ... 1994 / 美国 / 犯罪 剧情2霸王别姬9.6导演: 陈凯歌 ... 1993 / 中国大陆 中国香港 / 剧情 爱情3阿甘正传9.5导演: 罗伯特·泽米吉斯 ... 1994 / 美国 / 剧情 爱情保存 CSV 时使用utf-8-sig编码而不是utf-8是为了防止 Excel 打开时中文乱码。这是一个非常实用的编码细节。4.5 如果你请求失败怎么办豆瓣的反爬策略偶尔会升级。如果你运行时就遇到403或418状态码可以尝试在HEADERS中补充 Cookie 请求头。方法是在浏览器中登录豆瓣网站按 F12 打开开发者工具在“网络”面板任意请求的“请求头”里复制 Cookie 值拼接到 HEADERS 中。不过要注意Cookie 有时效性这只是临时调试手段。同时如果页面结构发生变化.select()的结果可能为空建议先抓一份 HTML 保存到本地再用 BeautifulSoup 解析确认选择器仍然有效。5. 高频踩坑与解决方案5.1 常见报错排查表问题现象常见原因解决思路403 Forbidden缺少 User-Agent 或被服务器识别为爬虫设置完整请求头降低请求频率必要时添加 Cookie 或使用代理池requests.exceptions.ConnectionError网络异常或目标网站主动拒绝检查本地网络添加重试机制暂停一会再试requests.exceptions.Timeout服务器响应慢或请求未设置超时给timeout传入具体值请求失败时自动跳过IndexError: list index out of range选择器没有匹配到元素页面结构变化单独抓取 HTML 文件调试选择器确认页面结构中文乱码页面编码与解析编码不一致或 CSV 编码问题优先设置response.encoding utf-8CSV 使用utf-8-sigModuleNotFoundError第三方库未安装执行pip install requests beautifulsoup4 lxml5.2 为什么会出现 403403 Forbidden是最常见的反爬信号。服务器通过请求头里的 User-Agent 判断请求是否来自浏览器。requests 默认的 User-Agent 是python-requests/x.x.x目标服务器一眼就能识别出这是程序于是直接拒绝。解决办法就是伪装成浏览器把 Chrome、Firefox 等浏览器的 User-Agent 复制到请求头中。如果加上 User-Agent 依然 403通常是因为请求频率太快或服务器开启了更严格的校验比如 Cookie、浏览器指纹这时候要放慢速度并考虑使用 Session 保持会话。5.3 页面动态加载怎么办有些网站的数据并不在 HTML 源码里而是通过 JavaScript 异步请求接口获取。遇到这种情况有两个思路分析网络请求打开 F12 的“网络”面板刷新页面找到 XHR 或 Fetch 类型的请求查看返回的 JSON 数据。这种接口通常可以直接用 requests 模拟请求获取数据比解析 HTML 更简单。使用 Selenium 或 Playwright这些工具会驱动真实浏览器渲染页面适合处理重交互页面但性能和稳定性不如直接请求接口。给新手的建议是优先看接口实在找不到接口再用浏览器自动化方案。5.4 如何优雅地处理异常很多刚入门的朋友写爬虫时只处理了正常情况一旦请求失败程序就崩了。推荐使用try...except包裹网络请求部分并在循环里增加失败重试和日志打印。比如上一节的fetch_page函数失败时返回None主程序不中断这就是最基本的容错思路。6. 爬虫进阶路线与工程化建议6.1 从入门到精通的进阶路线完成上面的案例后你其实已经掌握了爬虫最基本的能力。接下来可以按下面的顺序继续深入数据解析进阶学习 XPath 语法、JsonPath以及正则表达式在提取数据中的应用。XPath 在某些场景下比 CSS 选择器更灵活。存储层进阶学会使用 MySQL、MongoDB 存储数据而不是只保存 CSV。框架层进阶系统学习 Scrapy 框架。Scrapy 是 Python 生态最成熟的爬虫框架内置并发、去重、请求调度的能力适合大规模采集。反爬应对进阶学习 Session 维持会话、Cookie 处理、验证码识别、代理 IP 使用、请求频率控制等知识。分布式爬虫结合 Redis 实现 URL 队列的分布式调度这是大规模爬虫的工程方向。要注意的是反爬对抗的水很深作为学习者重心不要放在“绕过对方的防护”上而应该放在“如何写出稳定、高效、可维护的代码”上。6.2 爬虫工程化的最佳实践如果以后你要在公司里写正式的爬虫项目下面这些建议值得牢记。先讲代码结构。不要把所有逻辑堆在一个文件里建议拆分成spider.py抓取逻辑、parser.py解析逻辑、pipeline.py存储逻辑、config.py配置项四部分。这样目标网站变了你只需要改对应模块其他模块不受影响。再讲请求频率。一定要把请求频率控制在目标网站可接受的范围内。常见的做法是在两次请求之间加上随机延时import random import time # 随机延时 1 到 3 秒模拟真实用户行为 time.sleep(random.uniform(1, 3))这个写法的好处是延时不再固定目标服务器不容易通过固定间隔识别出爬虫。然后是日志。不要只靠print输出调试信息。Python 自带的logging模块可以同时输出到控制台和文件方便排查线上问题。建议每次请求记录时间、URL、状态码、耗时出现异常时记录完整堆栈。再然后是数据清洗。从网页抓下来的文本往往带有大量空白字符和噪声建议在解析阶段就完成数据清洗而不是存到数据库后再处理。清洗规则包括去首尾空格、合并多个空格、去除换行符、统一日期格式等。关于重试机制可以封装一个简单的重试装饰器import time from functools import wraps def retry(max_retries3, delay2): 简单的请求重试装饰器建议只在测试环境使用 def decorator(func): wraps(func) def wrapper(*args, **kwargs): for attempt in range(max_retries): try: return func(*args, **kwargs) except Exception as e: print(f第 {attempt 1} 次请求失败: {e}) if attempt max_retries - 1: raise time.sleep(delay) return wrapper return decorator最后是安全意识。爬虫代码里如果涉及登录状态不要把用户名密码硬编码在代码中应使用环境变量或配置文件管理敏感信息。凡是涉及账号、Token、Cookie 的操作都要遵守最小权限原则仅使用当前任务需要的权限。6.3 数据存储的取舍初学者最常用的存储方式有两种CSV适合数据量小、结构简单的场景方便用 Excel 查看。SQLite适合需要简单查询和去重的场景Python 内置sqlite3模块不需要额外安装数据库服务。数据量一旦超过百万条或者需要多人同时读写就应该考虑 MySQL 或 MongoDB。在选择数据库时不要追求功能全面而要根据数据的结构化程度、查询需求和团队技术栈来定。7. 本文掌握了什么下一步怎么学到这里你已经完成了一次完整的爬虫入门理解了爬虫的工作流程安装了 Python 环境与 requests、BeautifulSoup 库掌握了 HTTP 基础概念和请求头配置写出了豆瓣 Top250 的完整抓取脚本并了解了常见报错的排查思路。我建议你先把文章里的案例自己动手敲一遍不要直接复制粘贴。手动敲代码的过程中你会自然地记住选择器的写法、异常处理的位置和 CSV 的编码细节。遇到报错不要慌对照第 5 节的排查表一步步来。接下来可以根据自己的方向选择进阶路径如果对数据分析有兴趣爬下来的数据配合 pandas、matplotlib 做可视化分析会很有趣如果对工程和效率更感兴趣去学 Scrapy 会打开新世界的大门。爬虫技术最需要的就是“多看多写多调试”保持好奇心遇到问题就查慢慢积累你会发现它并不神秘。如果这篇文章对你有帮助可以收藏备用也欢迎在评论区分享你遇到的爬虫报错和解决方法。