零基础学Python爬虫:18个实战案例掌握数据采集全流程
2026/9/10 22:23:57 网站建设 项目流程

很多人学 Python 爬虫,卡住的地方不是语法,而是教程太碎。今天看一个单页爬虫,明天看一个 JS 逆向,到了最后,连一个完整的、能从“打开网页”走到“存好数据”的项目都没做完。如果你也有这种感觉,这篇文章就是给你准备的。

这里有一个明确判断:18 个爬虫实战案例不是用来凑数量的,而是用越来越贴近真实项目的例子,把“请求、解析、清洗、存储”这条链路彻底走通。对这些案例,不需要会 JavaScript,不需要懂逆向工程,只需要 Python 基础语法、requests 和 BeautifulSoup,再加上一点耐心。

读完这篇文章,你能得到三个结果:第一,理解网页数据从产生到落盘的完整链路;第二,拿到可以直接运行的 5 组核心源码,并看懂它们背后的通用套路;第三,知道遇到翻页、JSON 接口、登录、反爬和增量更新时,应该往哪个方向思考。

1. 为什么说 18 个案例比刷三遍语法更有用

Python 爬虫的学习路径,和 Python 基础语法的学习路径很不一样。语法学习是“记住规则 + 做练习”,爬虫学习是“遇到问题 + 拆解问题 + 把网页解构成数据”。

很多零基础读者习惯先把 Python 教程看完再动手,结果看完requests 和 BeautifulSoup 的文档,打开一个真实网页仍然不知道从哪里下手。原因很简单:文档讲的是“库能干什么”,实战讲的是“遇到一个具体网页,先看哪里,再写什么”。

爬虫实战需要三层能力,这三层能力只能通过案例来训练:

能力层级解决的问题对应训练
请求层拿到网页源码或接口数据requests、URL 规律、请求头
解析层从 HTML 或 JSON 中提取目标字段BeautifulSoup、正则、json 解析
存储层把数据保存成 CSV、Excel 或数据库csv、pandas、sqlite3

只看概念,读者会误以为爬虫的难度主要在“反爬”上。但从教学视角看,80% 的初学者卡在请求层的 URL 拼接和解析层的选择器编写上。18 个案例的设计思路,就是先把这两个基础缺口补上,再逐步加入翻页、接口、登录态和增量更新等真实场景。

什么样的人最适合按这套案例学习?

  • 刚学完 Python 基础语法,想做点东西验证能力的人。
  • 工作中需要从网页手动复制数据,想用脚本替代重复操作的人。
  • 想转行数据分析岗位,需要自己采集练习数据的人。
  • 被各种“爬虫速成课”误导,想重新打好请求和解析基本功的人。

这套案例不适合谁?不适合想抓取 App 加密数据、想绕过验证码、想大规模采集商业平台数据的人。原因不是技术做不到,而是风险和法律边界不划算。这篇文章讲的是安全、合规、可持续的爬虫入门方式。

2. 爬虫的核心概念与工作原理

2.1 抓网页就像给服务器发一张“明信片”

爬虫的本质只有一个动作:向服务器发送 HTTP 请求,然后接收响应。服务器返回的响应通常有两种形式:一种是 HTML 页面,需要解析;一种是 JSON 数据,直接提取即可。

用类比来说,浏览器访问网页是“人在看”,爬虫访问网页是“程序在看”。服务器并不关心对面是谁,它只关心请求是否符合规则。

一个最基本的 requests 请求只需要三行代码:

import requests url = "https://example.com" resp = requests.get(url) print(resp.status_code) print(resp.text[:200])

2.2 什么是批量型、增量型和垂直型爬虫

在搜索材料中经常看到这三个词:“批量型爬虫、增量型爬虫、垂直型爬虫”。它们是爬虫按使用场景做的分类,理解了这个分类,就理解了 18 个案例的编排逻辑。

批量型爬虫一次抓取大量历史数据,通常从第 1 页开始逐页抓取,直到最后一页。它适合做初始数据积累,比如抓取一个商品分类下所有历史在售记录。缺点是如果网站每天新增数据,每次全量抓取成本高、频率快,容易被限流。

增量型爬虫只抓取上次抓取之后新增或变化的数据。它需要记录“上次抓到哪里”,常用方式包括数据库去重、记录最新 ID、记录最新时间戳。适合日报、新闻、监控类场景。

垂直型爬虫只关注某个特定领域或站点,比如只抓图书信息、只抓房源信息、只抓某个招聘网站的职位。这类爬虫的特点是目标固定、字段清晰、解析逻辑单一,非常适合入门练习。

类型抓取范围典型场景难度
批量型历史全量数据初次建库、离线分析★★
增量型新增和变化数据日报监控、价格跟踪★★★
垂直型特定网站/领域信息采集、竞品观察★★

2.3 解析 HTML 的核心思路

拿到 HTML 之后,解析核心不是写正则,而是用选择器找节点。BeautifulSoup 提供了 find 和 find_all 两个最常用的方法,配合标签名、class、id 可以定位到任何一处内容。

from bs4 import BeautifulSoup soup = BeautifulSoup(html_text, "html.parser") title = soup.find("h1").get_text() items = soup.find_all("div", class_="item")

初学者最容易犯的错误是“试图用一次 find_all 拿到所有答案”。实际更可靠的写法是:先定位到每条数据的容器节点,再在每个容器内继续查找字段。这个思路会反复出现在后面的案例里。

3. 环境准备与前置条件

3.1 Python 版本

建议使用 Python 3.8 以上版本。不同教程对版本要求不同,但本文案例使用的 requests、BeautifulSoup 库在 3.8 到 3.12 之间都能正常工作。版本请以实际环境为准,不用刻意追求最新。

Windows 用户可以从官网下载安装包,安装时务必勾选“Add Python to PATH”。Linux 用户可以通过系统包管理工具安装,安装完成后在终端里验证:

python --version pip --version

如果 pip 命令找不到,可以尝试:

python -m pip --version

3.2 安装依赖库

本文需要三个第三方库:requests 用于发请求,beautifulsoup4 用于解析 HTML,lxml 用于提高解析速度。安装命令如下:

pip install requests beautifulsoup4 lxml

国内网络环境下,如果 pip 下载速度慢,可以临时使用镜像源:

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

3.3 推荐 IDE 与项目结构

对零基础读者,推荐 VSCode 配合 Python 插件,或者直接用 PyCharm 社区版。本文案例的代码文件建议按功能来组织:

python-spider-cases/ ├── 01_download_one_image.py ├── 02_batch_download_images.py ├── 03_weather_api.py ├── 04_douban_top250.py ├── 05_incremental_news.py └── requirements.txt

把爬虫脚本拆成多个小文件,而不是写在一个文件里,能帮助读者在出错时快速定位问题,也为后续把脚本改造成类或模块留好空间。

4. 18 个实战案例总览

这 18 个案例按照“从简单到复杂、从静态到动态、从 HTML 到 JSON、从单页到批量”的顺序排列。需要注意:示例站点可能会改版,部分 URL 和网页结构可能变化,运行代码时以实际页面为准。判断依据是协议与字段含义,而不是某个固定字符串。

序号案例爬取目标核心知识点难度
1下载单张图片图片文件requests 字节流写入
2批量下载表情包/壁纸图片链接集合循环 + 列表存储
3天气数据查询JSON 接口requests.get + json 解析
4新闻列表抓取标题/时间/链接find_all + get_text
5豆瓣电影 Top250 第一页电影标题/评分/简介BeautifulSoup 选择器★★
6豆瓣电影 Top250 全部250 条记录翻页 URL 参数★★
7CSDN 博客文章标题采集标题/链接class 选择器 + 列表★★
8招聘网站职位列表职位名/公司/薪资列表页字段提取★★
9京东商品搜索结果商品名/价格URL 参数 + 请求头★★
10手机 App 接口数据JSON 字段接口 URL 分析★★
11微博热搜榜热搜词/热度移动端页面解析★★
12知乎问题回答数据回答内容/点赞数API 返回 JSON★★★
13B站视频信息标题/播放量/弹幕量网页 JSON 字段★★★
14微信文章信息文章标题/公众号搜狗微信搜索解析★★★
15链家在售房源小区/户型/价格翻页 + 详情字段★★★
16汽车之家车型参数参数表表格解析★★★
17酒店价格信息酒店名称/价格复杂站点反爬思路★★★
18增量爬虫综合案例新增新闻/更新记录去重 + 时间戳★★★

从学习角度,第 1 到第 4 个案例用来建立信心,第 5 到第 9 个案例用来掌握主流解析套路,第 10 到第 14 个案例用来学习不同数据格式,第 15 到第 18 个案例用来接触真实复杂场景。

5. 首批入门案例:从单张图片到批量下载

5.1 案例 1:单张图片下载

图片下载和文本下载本质相同,区别在于图片需要以二进制方式写入文件。

# 文件路径:01_download_one_image.py import requests url = "https://img.example.com/sample.jpg" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } resp = requests.get(url, headers=headers, timeout=10) if resp.status_code == 200: with open("sample.jpg", "wb") as f: f.write(resp.content) print("图片下载成功,大小:", len(resp.content), "字节") else: print("下载失败,状态码:", resp.status_code)

说明:

  • resp.content返回的是二进制内容,不能用resp.text保存图片。
  • 设置timeout=10是为了防止某个请求卡死导致程序无响应。
  • User-Agent是一个声明自己是什么客户端的请求头,不同站点对默认 UA 的处理方式不同,加上 PC 浏览器 UA 更稳妥。

5.2 案例 2:批量下载表情包

批量下载的核心是先拿到所有图片的链接,再通过循环逐一下载。

# 文件路径:02_batch_download_images.py import requests from bs4 import BeautifulSoup base_url = "https://example.com/funny" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } resp = requests.get(base_url, headers=headers, timeout=10) soup = BeautifulSoup(resp.text, "html.parser") img_tags = soup.find_all("img") img_urls = [] for img in img_tags: src = img.get("src") if src and src.startswith("http"): img_urls.append(src) print("找到图片数量:", len(img_urls)) for index, img_url in enumerate(img_urls[:10]): r = requests.get(img_url, headers=headers, timeout=10) if r.status_code == 200: with open(f"image_{index}.jpg", "wb") as f: f.write(r.content) print(f"已下载第 {index + 1} 张图片")

5.3 这批案例的小结论

前 4 个案例在难度上不需要任何框架知识,但它们完成了两件重要的事:让读者写出第一个“发请求并保存数据”的完整流程,以及理解“URL 是输入,文件是输出”的最小闭环。真正让爬虫变得有价值的是后面的批量化和自动化,而不是单次请求本身。

6. 列表页与翻页案例:豆瓣电影 Top250

豆瓣电影 Top250 是中文爬虫学习中最经典的案例之一。它的页面结构稳定、数据字段清晰、服务器响应友好,非常适合用来练习列表页解析和翻页循环。

6.1 观察 URL 规律

打开豆瓣电影 Top250,第一页 URL 是:

https://movie.douban.com/top250?start=0&filter=

第二页把start=0改成start=25,第三页改成start=50。这说明豆瓣使用的是“start 偏移量”翻页方式,每页显示 25 条,总共 10 页。

6.2 完整源码

# 文件路径:04_douban_top250.py import requests from bs4 import BeautifulSoup import csv import time headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0 Safari/537.36" } def fetch_one_page(start): url = f"https://movie.douban.com/top250?start={start}&filter=" resp = requests.get(url, headers=headers, timeout=10) resp.raise_for_status() soup = BeautifulSoup(resp.text, "html.parser") items = soup.find_all("div", class_="item") rows = [] for item in items: title_tag = item.find("span", class_="title") title = title_tag.get_text() if title_tag else "" rating_tag = item.find("span", class_="rating_num") rating = rating_tag.get_text() if rating_tag else "" quote_tag = item.find("p", class_="quote") quote = quote_tag.get_text().strip() if quote_tag else "" rows.append([title, rating, quote]) return rows def main(): all_rows = [] for start in range(0, 250, 25): print(f"正在抓取 start={start} 的页面") rows = fetch_one_page(start) all_rows.extend(rows) time.sleep(2) with open("douban_top250.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["标题", "评分", "短评"]) writer.writerows(all_rows) print("抓取完成,共保存", len(all_rows), "条记录") if __name__ == "__main__": main()

运行方式:

python 04_douban_top250.py

运行成功后,当前目录下会出现douban_top250.csv,可以用 Excel 打开。使用utf-8-sig编码是因为 Windows 下的 Excel 对无 BOM 的 UTF-8 文件可能出现中文乱码。

这里最值得学习的不是豆瓣本身,而是其中三个通用套路:

  • range(0, 250, 25)拼接所有页面 URL。
  • time.sleep(2)控制请求节奏,避免对目标站造成压力。
  • csv.writer把内存中的结构化数据保存到本地文件。

6.3 翻页案例的常见问题

如果运行后发现 CSV 里只有表头没有数据,最常见原因是find_all("div", class_="item")返回空列表。这意味着页面结构已经变化,或者请求被重定向到了登录页。第一步应该打印resp.status_coderesp.text[:500]来确认响应内容到底是什么。

7. JSON 接口案例:天气数据与热搜数据

不是所有网站都适合直接解析 HTML。很多网站的页面数据是通过 JavaScript 异步请求加载的,这种接口返回的通常是 JSON。对爬虫来说,JSON 接口比 HTML 页面更容易处理,因为字段结构是现成的,不需要猜选择器。

7.1 天气数据接口示例

# 文件路径:03_weather_api.py import requests import json city_code = "101010100" # 城市代码,这里以北京为例 url = f"https://www.example-weather-api.com/weather?city={city_code}" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } resp = requests.get(url, headers=headers, timeout=10) if resp.status_code == 200: data = resp.json() print(json.dumps(data, ensure_ascii=False, indent=2))

说明:

  • resp.json()会把响应体直接解析成 Python 字典或列表。
  • json.dumps(..., indent=2)用于格式化打印,方便人工查看结构。
  • 不同天气服务的字段命名不一样,需要先打印一份完整 JSON 再决定取哪些字段。

7.2 JSON 接口的通用提取代码模板

def extract_fields(data, path): current = data for key in path: if isinstance(current, list): current = current[int(key)] else: current = current.get(key) return current

这个模板支持类似["data", "list", "0", "title"]的多级路径提取,适合在调试接口时快速验证字段位置。

7.3 什么时候优先找 JSON 接口

当目标页面打开时,如果页面内容不是一次性返回,而是先看到空白框架、然后逐步渲染出来,就需要优先在网络面板中查找 JSON 接口。判断方法:打开浏览器开发者工具,切换到 Network 面板,刷新页面,筛选XHRFetch,再逐个查看响应内容。这个习惯对后面抓取微博热搜、知乎回答、B站信息非常有用。

8. 需要登录和反爬的案例思路

第 11 到第 18 个案例中,有一部分会涉及登录状态、请求头限制或访问频率限制。下面从通用角度讲清楚处理思路,不涉及任何绕过机制,也不提供破解手段。

8.1 使用 Session 保持会话

如果目标页面需要登录后访问,可以使用 requests 的 Session 对象来保持 Cookie。

import requests session = requests.Session() session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" }) # 模拟登录(仅作为示例,真实登录参数以目标站为准) payload = { "username": "your_username", "password": "your_password", } login_url = "https://example.com/login" session.post(login_url, data=payload, timeout=10) # 用同一个 Session 访问需要登录的页面 resp = session.get("https://example.com/profile", timeout=10) print(resp.status_code)

注意:登录接口的字段名、加密方式、验证码要求因站而异。本文只演示 Session 保持登录的思路,具体参数需要从开发者工具的 Network 面板中获取。

8.2 设置 User-Agent 和超时重试

很多站点会拦截不带 User-Agent 或 User-Agent 明显异常的请求。推荐的请求头设置方式:

import requests from requests.adapters import HTTPAdapter session = requests.Session() session.mount("http://", HTTPAdapter(max_retries=3)) session.mount("https://", HTTPAdapter(max_retries=3)) headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0 Safari/537.36", "Accept": "text/html,application/json,text/plain,*/*", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" } session.headers.update(headers)

8.3 遇到验证码的正确处理方式

如果遇到验证码、滑块校验或者“访问频率过高”的提示,说明目标站点的风控机制已经触发。此时应该做三件事:

  1. 立即暂停脚本,而不是继续提高请求频率。
  2. 降低抓取频率,把请求间隔从 1 秒提升到 5 秒以上。
  3. 评估数据源。如果只是想学习,可以换成没有风控的练习站点;如果是业务需求,则优先考虑官方 API 或数据授权合作。

这个原则同样适用于手机 App 数据、电商平台数据和社交平台数据。爬虫的价值在于提高学习效率和信息获取效率,而不是破解别人部署的防护系统。

9. 增量型爬虫实现思路

在 18 个案例中,第 18 个案例是增量爬虫。它和前 17 个案例最大的区别是:前 17 个跑一次就结束,增量爬虫需要反复运行,并且每次只抓取新增数据。

9.1 增量爬虫的核心:记住“上次抓到哪里”

常见的记录方式有三种:

方式适用场景优缺点
根据 ID 去重列表型数据简单可靠,需要维护已抓取 ID 集合
根据时间戳新闻、公告需要目标页面提供时间字段
根据内容哈希无法确定唯一键通用但计算开销较大

9.2 一个简单的增量去重框架

import json import os CACHE_FILE = "seen_ids.json" def load_seen_ids(): if os.path.exists(CACHE_FILE): with open(CACHE_FILE, "r", encoding="utf-8") as f: return set(json.load(f)) return set() def save_seen_ids(seen_ids): with open(CACHE_FILE, "w", encoding="utf-8") as f: json.dump(list(seen_ids), f, ensure_ascii=False) def fetch_latest_items(): # 替换为真实的抓取逻辑 return [ {"id": "1001", "title": "新闻一"}, {"id": "1002", "title": "新闻二"}, {"id": "1003", "title": "新闻三"}, ] def main(): seen = load_seen_ids() items = fetch_latest_items() for item in items: if item["id"] not in seen: print("新增:", item["id"], item["title"]) # 到这里可以保存到数据库或写入文件 seen.add(item["id"]) save_seen_ids(seen) print("完成,已记录", len(seen), "个 ID") if __name__ == "__main__": main()

这个框架把去重逻辑和抓取逻辑分开,读者在真实项目里只需要替换fetch_latest_items()返回的数据即可。

9.3 增量爬虫的工程建议

  • 去重状态值存到文件、SQLite 或 Redis,不要每次重新初始化。
  • 每次运行先记录开始时间,结束后记录本次处理条数。
  • 异常中途退出时,应该保留已经写入的数据,避免重复抓取。

10. 常见问题与排查方法

下面表格汇总了入门阶段最常遇到的 8 个问题。遇到问题时,按表格中的“排查方式”先看现象,再定位原因。

问题现象可能原因排查方式解决方案
中文乱码编码判断错误打印resp.encodingresp.apparent_encoding手动设置resp.encoding = "utf-8"
SSL 证书报错目标站证书不受信任检查 URL 是否为 https不要直接关闭验证,先检查系统时间与证书
找不到数据节点页面结构已变打印resp.text[:1000]重新检查真实页面的 HTML 结构
返回 403请求被拒绝查看 response headers更新 User-Agent、Accept 等请求头
返回 404URL 拼接错误检查 URL 参数打开浏览器查看实际请求地址
请求超时目标服务器响应慢增加 timeout 值设置timeout=(5, 10)并在异常中重试
列表为空选择器写错用选择器工具验证改用 find_all + 容器节点逐层查找
被限流封禁请求频率过高查看响应中的提示信息增大 sleep 间隔,加随机延时

排查通用顺序是:先看状态码,再看响应文本前 500 字,最后检查选择器和 URL。不要一上来就怀疑反爬。

11. 最佳实践与工程建议

11.1 请求频率:把“礼貌”写进代码

无论目标站是练习站点还是真实站点,都应该控制请求频率。一个简单的限速写法:

import time import random def polite_sleep(): time.sleep(random.uniform(1.5, 3.5))

随机间隔比固定间隔更接近真实用户行为,也能显著降低触发限流的概率。

11.2 异常处理:不要一个请求失败就中断整个任务

正确做法是捕获异常、记录失败 URL、跳过当前请求、继续后续任务。

import logging logging.basicConfig(level=logging.INFO, filename="spider.log") def safe_get(url, session, retries=3): for attempt in range(retries): try: resp = session.get(url, timeout=10) if resp.status_code == 200: return resp except Exception as e: logging.warning(f"第 {attempt + 1} 次请求失败: {url}, 错误: {e}") time.sleep(2) return None

11.3 数据存储:小项目用 CSV,大项目用数据库

前 18 个案例使用 CSV 或 JSON 文件就够了。实际项目如果数据量超过几万条,建议切换到 SQLite 或 MySQL,并给唯一键加索引。

11.4 日志记录:让脚本“事后可查”

每个脚本至少应该记录三件事:请求 URL、响应状态码、保存文件路径。这样即使第二天发现数据不对,也能快速定位是哪个环节出了问题。

11.5 法律与合规边界

这一点必须反复强调:

  • 爬取公开信息用于学习研究没问题,但要注意尊重目标网站的 robots.txt 声明。
  • 不能抓取用户个人隐私信息、账号信息、非公开接口数据。
  • 爬取的数据不能用于商业转售、批量导出后发布、恶意竞争等用途。
  • 抓取频率要控制在对服务器无明显影响的范围内。
  • 遇到验证码、登录墙、风控拦截时,应停止尝试,而不是继续使用更激进的方式。

判断一个爬虫项目是否合适,有一个简单标准:你希望别人用同样的方式爬你自己的网站吗?如果答案是否定的,就不要对别人做同样的事。

12. 总结与后续学习方向

这 18 个案例真正想讲清楚的,是爬虫学习中反复出现的通用套路:URL 是入口,请求头是身份证明,响应体是原材料,解析逻辑是加工线,CSV/数据库是成品仓库。把这五步走通一次,后面的学习会轻松很多。

对零基础读者,建议不要一口气写 18 个案例。先复制第 5 章的单图片下载代码,跑通;再把第 6 章的豆瓣 Top250 代码完整运行一遍,观察 CSV 结果;最后用第 9 章的增量去重框架改造成自己的“每日新闻提醒脚本”。完成这三个步骤,再回头补充其他案例,效率会明显更高。

后续可以继续深入的方向有三个:

  • 学习 scrapy 框架,理解批量爬虫的项目工程化方式。
  • 学习 pandas,把爬取的数据直接做清洗与分析。
  • 学习反爬应对的合法边界,重点理解请求头、Cookie、代理池和验证码背后的风控逻辑,而不是绕过手段。

如果你在运行代码时遇到报错,先复制错误信息去搜索,再看代码里的函数名和缩进。绝大多数问题不是“太难”,而是漏了一个冒号、选错了一个标签名。保持动手习惯,18 个案例跑完之后,你会发现自己已经能看懂很多开源爬虫项目的源码了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询