Python 爬虫入门到可用:请求、解析、反爬与合规
从一次真实抓取需求出发,讲清 requests 请求构造、BeautifulSoup / CSS 选择器解析、分页与翻页、登录态维持、常见反爬的应对边界,以及采集时必须守住的合规底线。

学习信息
Python 爬虫:从能跑到能用
适合谁
- 做电商需要看竞品价格、上新节奏、评价变化的人
- 做内容需要批量收集素材、标题、评论的人
- 已经会 Python 基础语法,想把「手动复制」变成「自动抓取」的人
先说清楚一件事:爬虫本身不违法,但怎么用有边界。这篇只讲公开页面的合理采集,以及遇到反爬时的正当处理方式。绕过登录鉴权抓取非公开数据、抓取后转售牟利、高频请求打垮对方服务器,这些都不在本文讨论范围内,也不建议任何人去做。
第一道门槛:请求头别偷懒
很多初学者写出的第一行代码就被拒,原因是默认请求头写着 python-requests,对方一眼识别。
import requests
HEADERS = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/126.0 Safari/537.36",
"Accept-Language": "zh-CN,zh;q=0.9",
}
resp = requests.get("https://example.com/list", headers=HEADERS, timeout=15)
resp.raise_for_status() # 非 2xx 直接抛错,别让程序带着烂数据往下跑
resp.encoding = resp.apparent_encoding # 中文站经常自我标错编码
print(resp.status_code, len(resp.text))timeout 一定要写。不写超时,遇到不响应的站点会把整个脚本卡死,跑批的时候最要命。
解析:优先 CSS 选择器,少写正则
网页结构用 BeautifulSoup 处理足够了,不要一上来就用正则匹配 HTML——标签嵌套一变,正则全废。
from bs4 import BeautifulSoup
soup = BeautifulSoup(resp.text, "lxml")
for item in soup.select("div.product-card"):
title = item.select_one("h3.title")
price = item.select_one("span.price")
print({
"title": title.get_text(strip=True) if title else "",
"price": price.get_text(strip=True) if price else "",
"url": item.get("href") or (item.select_one("a") or {}).get("href", ""),
})select_one 可能返回 None,一定要判空。真实网页里缺图、缺价的商品到处都是,不判空的脚本跑到第 300 条就崩。
翻页的三种形态
| 形态 | 特征 | 处理方式 | | --- | --- | --- | | URL 带页码 | ?page=2 | 循环拼 URL,直到返回空列表停止 | | 相对偏移 | ?offset=20 | 步长固定,累加 offset | | 滚动加载 / 无页码 | 点「加载更多」才出数据 | 抓 XHR 接口,或上 Playwright |
遇到第三种,优先打开浏览器开发者工具的 Network 面板,看 XHR 请求返回的是不是 JSON。能直接拿 JSON 就不要解析 HTML,省事且稳定得多。
# 接口型分页:直接拿结构化数据
params = {"page": 1, "pageSize": 20}
r = requests.get("https://example.com/api/items", params=params,
headers=HEADERS, timeout=15)
data = r.json()
items = data.get("data", {}).get("list", [])反爬:先降速,再谈其他
被封的第一反应不应该是找代理池,而是先问自己:我是不是请求太快了?
1. 加间隔:time.sleep(random.uniform(1, 3)),别用固定值,太规律反而异常 2. 复用会话:session = requests.Session(),自动维持 Cookie,减少握手 3. 尊重 robots.txt:先看目标站根目录的 robots.txt,明确禁止的目录不要碰 4. 识别 vertebrates 验证码就停:弹出验证码说明对方已经注意到你,继续硬闯只会换来封 IP
import time, random, requests
session = requests.Session()
session.headers.update(HEADERS)
for page in range(1, 11):
r = session.get(f"https://example.com/list?page={page}", timeout=15)
# ... 处理逻辑
time.sleep(random.uniform(1.2, 2.8))什么时候该上浏览器自动化
页面数据必须执行 JS 才生成、或者接口参数被加了签名,这时候再考虑 Playwright / Selenium。代价是速度慢几十倍、内存占用高,能用接口解决就别上浏览器。
数据落地与断点续采
抓到的数据别只在内存里,随时写盘,中断了才能续。
import csv, os
FILE = "items.csv"
exists = os.path.exists(FILE)
with open(FILE, "a", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=["title", "price", "url"])
if not exists:
writer.writeheader()
writer.writerows(items)用 utf-8-sig 是为了让 Excel 直接打开不乱码。断点续采的最简做法:先读已有 CSV 把主键读进集合,抓到重复就跳过。
合规红线(务必看完)
- 只采集公开可见数据,绕过登录、破解接口鉴权、利用越权漏洞抓取内部数据,均可能构成违法
- 不要采集个人信息,用户昵称、手机号、收货地址等一旦涉及法律风险陡增
- 控制频率,以不影响对方正常运行为准;造成服务瘫痪可能构成破坏计算机信息系统罪
- 不要搬运牟利,把抓来的内容原样发布售卖,涉及著作权侵权
- 商业用途采集前,优先查看对方是否提供开放 API —— 能用官方 API 就用官方的
常见问题
返回 403 但浏览器能打开? 先补齐 Referer 和完整 UA;仍不行说明有风控,降低频率或换官方 API。
拿到的是乱码? 设置 resp.encoding = resp.apparent_encoding;gzip 未解压时检查响应头。
中文 CSV 乱码? 写入时用 encoding="utf-8-sig"。
下一步
- 批量去重与增量更新:把主键存 SQLite,每次只写新增
- 定时任务:Mac/Linux 用 crontab,Windows 用任务计划程序
- 大规模采集:再去看 Scrapy 框架,先用原生 requests 把单站点跑通更重要
常见问题
下载链接失效怎么办?
请优先切换其他网盘渠道,并反馈失效链接。
会员能下载所有资料吗?
页面标注为会员专享的资料支持会员下载,具体以当前页面显示为准。
相关资源
MCP Server 开发实战:从零构建标准化工具服务
手把手教你开发 MCP Server,实现 tools/list、tools/call、resources/list、resources/read、prompts/list、prompts/get 完整协议,支持 stdio 和 SSE 传输,包含测试、部署、发布最佳实践。
Python 零基础入门教程(2025 完整版)
面向零基础学员的 Python 编程入门教程,覆盖环境搭建、基础语法、流程控制、函数、模块、面向对象、常用库(requests/numpy/pandas),附带 50+ 实战小项目和面试题。
Python 批量处理 Excel 实战(openpyxl + pandas)
面向每天和表格打交道的运营、财务、跨境电商从业者,用 Python 自动完成多表合并、清洗、公式填充、图表输出与定时跑批,把重复的表格操作压缩成一条命令。
pandas 数据分析实战:从读表到出结论
用一条完整的分析链路串起 pandas 核心能力——数据读取与类型修整、缺失值与异常值处理、分组聚合、表连接、时间序列重采样,最后用透视表回答真实业务问题。
评论建议
欢迎反馈资料质量、链接状态或希望补充的内容,管理员会持续维护。