Python爬虫基础:Requests库入门、HTTP协议、GET与POST请求、响应解析
说实话,很多人一听到「爬虫」就觉得是黑科技。其实说白了,爬虫就是模拟浏览器去服务器拿数据。你每天刷网页、看新闻、查天气,背后都是这套逻辑。我刚开始学的时候也犯过嘀咕——不就是发个请求吗?后来踩了几个坑才明白,这里面的门道还真不少。
这一章,咱们就把爬虫最核心的底子打牢。你不需要记住所有细节,但得理解整个流程是怎么转起来的。
本章核心目标:理解HTTP请求与响应的完整生命周期,掌握Requests库的基本用法,能独立完成GET/POST请求并解析返回数据。
1. HTTP协议——爬虫的「交通规则」
爬虫和服务器之间怎么沟通?靠的就是HTTP协议。你可以把它想象成快递行业的统一标准——包裹怎么打包、面单怎么填、快递员怎么敲门,都有规矩。
HTTP协议的核心就两样东西:请求和响应。你发一个请求过去,服务器给你一个响应回来。就这么简单。
1.1 URL——你要去哪
URL就是网址,告诉爬虫「目标在哪」。我见过不少新手直接把浏览器地址栏的链接复制过来就用,结果发现拿不到数据。为什么?因为有些链接里带了跟踪参数或者会话ID,换个环境就失效了。
一个标准的URL长这样:
https://api.example.com:443/data?page=1&limit=20#section
|______||______________||_||____||______________||_______|
协议 主机名 端口 路径 查询参数 片段
实际爬虫里,我们最常打交道的是查询参数。翻页、筛选、排序,全靠它。
1.2 状态码——服务器在说什么
每次请求,服务器都会返回一个三位数的状态码。我刚开始爬数据时,看到4xx就慌,后来才发现大部分问题都是自己粗心造成的。
| 状态码 | 含义 | 常见原因 |
|---|---|---|
| 200 | OK | 请求成功,数据正常返回 |
| 301/302 | 重定向 | URL变了,Requests默认会自动跟随 |
| 403 | Forbidden | 服务器拒绝访问,大概率是没加请求头 |
| 404 | Not Found | 路径写错了,或者资源被删了 |
| 429 | Too Many Requests | 爬太快被限制了,加个延时就好 |
| 500 | 服务器内部错误 | 不是你的问题,是服务器炸了 |
我的小习惯:每次请求后先检查 response.status_code,等于200再往下处理。别一股脑解析,万一服务器返回的是错误页面,你解析半天也是白费功夫。
2. Requests库——Python里最好用的HTTP工具
Python里发HTTP请求的库不少,但Requests是王者。它的口号是「HTTP for Humans」,说白了就是让你用最少的代码干最多的活。
安装就一句话:
pip install requests
然后导入就能用:
import requests
3. GET请求——从服务器拿数据
GET是最常用的请求方式。你打开一个网页,浏览器发的就是GET请求。它的特点是:参数都挂在URL后面,肉眼可见。
3.1 最简单的GET
import requests
url = "https://httpbin.org/get"
response = requests.get(url)
print(response.text)
跑一下,你会看到服务器返回了一大段JSON。这就是最原始的响应内容。
3.2 带参数的GET
翻页、搜索、过滤,都需要传参数。我习惯用 params 参数来传,Requests会自动帮你拼接到URL上:
url = "https://httpbin.org/get"
params = {
"page": 1,
"limit": 20,
"keyword": "爬虫"
}
response = requests.get(url, params=params)
print(response.url) # 看看实际请求的URL长什么样
输出结果:
https://httpbin.org/get?page=1&limit=20&keyword=%E7%88%AC%E8%99%AB
注意那个 %E7%88%AC%E8%99%AB,这是中文被URL编码后的结果。Requests帮你自动处理了,不用自己操心。
我曾经踩过的坑:手动拼接参数时忘了URL编码,结果中文参数传过去服务器根本不认。后来一律用 params 字典,再也没出过问题。
4. POST请求——向服务器提交数据
POST和GET最大的区别在于:数据放在请求体里,不在URL上。登录、提交表单、上传文件,都用POST。
4.1 表单数据提交
大部分网站的表单提交,用 data 参数就行:
url = "https://httpbin.org/post"
form_data = {
"username": "admin",
"password": "123456"
}
response = requests.post(url, data=form_data)
print(response.text)
4.2 JSON数据提交
现在很多API接口都要求传JSON格式的数据。这时候用 json 参数,Requests会自动帮你做两件事:把字典转成JSON字符串,设置 Content-Type 头为 application/json。
url = "https://httpbin.org/post"
json_data = {
"name": "张三",
"age": 28,
"skills": ["Python", "爬虫", "GIS"]
}
response = requests.post(url, json=json_data)
print(response.text)
关键区别:用 data 传的是表单格式(application/x-www-form-urlencoded),用 json 传的是JSON格式(application/json)。服务器怎么要求的,你就怎么传,搞错了会返回400错误。
5. 响应解析——拿到数据怎么处理
请求发出去了,响应也回来了。接下来就是解析。我见过有人拿到响应直接打印,然后手动复制粘贴——千万别这么干,太原始了。
5.1 响应对象的核心属性
| 属性/方法 | 说明 | 使用场景 |
|---|---|---|
response.status_code |
状态码 | 判断请求是否成功 |
response.text |
字符串形式的响应内容 | HTML页面、普通文本 |
response.json() |
解析为Python字典/列表 | API接口返回的JSON数据 |
response.content |
字节形式的响应内容 | 图片、文件、二进制数据 |
response.headers |
响应头字典 | 查看服务器信息、编码方式 |
response.encoding |
编码方式 | 乱码时手动指定编码 |
5.2 处理JSON响应
现在大部分API都返回JSON。用 .json() 方法直接转成Python对象,操作起来特别方便:
url = "https://api.github.com/users/octocat"
response = requests.get(url)
if response.status_code == 200:
data = response.json()
print(f"用户名: {data['login']}")
print(f"仓库数: {data['public_repos']}")
print(f"粉丝数: {data['followers']}")
else:
print(f"请求失败,状态码: {response.status_code}")
5.3 处理乱码问题
你想想看,为什么有时候爬回来的中文全是乱码?因为服务器返回的编码和Python默认的编码对不上。Requests会尝试从响应头里猜编码,但有时候猜不准。
我的做法是:先看 response.encoding,如果不对就手动指定:
response = requests.get(url)
response.encoding = 'utf-8' # 或者 'gbk'、'gb2312'
print(response.text)
一个小技巧:如果不知道编码是什么,可以用 response.apparent_encoding,Requests会基于内容自动推断。不过这个操作比较耗时,生产环境里我一般直接指定。
6. 请求头——别让服务器认出你是爬虫
很多网站会检查 User-Agent,发现不是浏览器就直接拒绝。我刚开始爬某个电商网站时,怎么都拿不到数据,后来加了个浏览器的User-Agent就好了。
url = "https://httpbin.org/headers"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept-Language": "zh-CN,zh;q=0.9",
"Referer": "https://www.google.com/"
}
response = requests.get(url, headers=headers)
print(response.text)
常见的请求头字段:
- User-Agent:标识客户端类型,必加
- Referer:告诉服务器从哪个页面跳过来的
- Cookie:维持登录状态
- Authorization:携带认证令牌
7. 实战:完整的爬虫请求流程
把上面这些串起来,就是一个完整的爬虫请求流程。我写了个模板,每次爬新网站都拿它改:
import requests
def fetch_data(url, params=None, headers=None, method="GET"):
"""
通用的请求函数
"""
default_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
if headers:
default_headers.update(headers)
try:
if method.upper() == "GET":
response = requests.get(url, params=params, headers=default_headers, timeout=10)
elif method.upper() == "POST":
response = requests.post(url, data=params, headers=default_headers, timeout=10)
else:
raise ValueError("不支持的请求方法")
response.encoding = 'utf-8'
if response.status_code == 200:
return response.json() if 'application/json' in response.headers.get('Content-Type', '') else response.text
else:
print(f"请求失败: {response.status_code}")
return None
except requests.exceptions.Timeout:
print("请求超时,请检查网络或目标服务器")
return None
except requests.exceptions.ConnectionError:
print("连接失败,请检查URL是否正确")
return None
# 使用示例
url = "https://api.example.com/data"
result = fetch_data(url, params={"page": 1})
if result:
print(result)
注意:一定要加 timeout 参数。我遇到过爬虫跑了一整夜,结果卡在一个死链接上,所有线程都挂在那。加了超时,至少能保证程序不会无限等待。
8. 常见问题与避坑指南
- SSL证书错误:有些网站证书过期或自签名,加
verify=False可以跳过验证,但会有警告。用requests.packages.urllib3.disable_warnings()关掉警告。 - 重定向:Requests默认自动跟随重定向,如果不想跟,设置
allow_redirects=False。 - 会话保持:用
requests.Session()可以保持Cookie,适合需要登录的网站。 - 代理:用
proxies参数设置代理,爬国外网站时经常用到。
嗯,到这里,Requests库的基础用法你应该心里有数了。说白了就是三步:构造请求、发送请求、解析响应。后面的章节我们会在这个基础上,一步步搭建真正能用的爬虫系统。