第 56 课:HTTP 请求与 requests 库
模块十二:网络编程 | 难度:intermediate | 标签:http, requests, web
学习目标
- 理解HTTP协议基础:了解GET、POST、PUT、DELETE等基本请求方法及其用途。
- 掌握
requests库的使用:学会使用requests库发送各种HTTP请求,并获取响应。 - 处理请求参数与响应数据:能够向URL添加查询参数,设置请求头,并正确解析JSON、文本等格式的响应数据。
- 了解常见的HTTP状态码:理解200、404、500等状态码的含义,用于调试和判断请求结果。
- 学会基本的错误处理:能够在代码中捕获网络请求可能发生的异常(如超时、连接错误)。
核心概念
什么是HTTP?
想象一下,你在浏览器地址栏输入一个网址(如https://api.github.com)并按下回车,浏览器就会向存放这个网站的服务器发送一个 HTTP请求。服务器处理完请求后,会将一个 HTTP响应 发送回你的浏览器,浏览器再将响应内容(如HTML页面、图片、JSON数据)渲染出来。
HTTP(超文本传输协议)就是浏览器(或你的Python程序)与服务器之间沟通的"语言"。它基于请求-响应模型,主要包含两种核心角色:
- 客户端:发起请求的一方(如你的浏览器或Python脚本)。
- 服务器:接收请求并返回响应的一方。
为什么选择 requests 库?
Python内置了一个名为 urllib 的库可以用来发送HTTP请求,但其API相对繁琐。requests 库由社区开发者Kenneth Reitz编写,它的理念是"为人类而生",提供了极其简洁、优雅且功能强大的API,极大地简化了HTTP请求的编写过程,是目前Python生态中处理HTTP请求的事实标准。
一个简单的对比:
# 使用内置的urllib(相对复杂)
import urllib.request
response = urllib.request.urlopen('https://api.github.com')
data = response.read().decode('utf-8')
# 使用requests库(非常直观)
import requests
response = requests.get('https://api.github.com')
data = response.text
代码示例
示例1:发送一个基本的GET请求
GET请求通常用于从服务器获取数据。
import requests
import json
# 1. 发送一个简单的GET请求
response = requests.get('https://httpbin.org/get')
# 2. 查看响应状态码 (200 表示成功)
print(f"状态码: {response.status_code}")
# 3. 查看响应头
print(f"Content-Type: {response.headers['Content-Type']}")
# 4. 获取响应内容
# response.text 返回解码后的字符串
print("响应文本(前100字符):", response.text[:100])
# 如果响应是JSON格式,可以使用.json()方法直接解析为字典
data = response.json()
print("\n解析后的JSON数据:")
print(json.dumps(data, indent=2, ensure_ascii=False)) # 美化打印
示例2:发送带参数的请求
查询参数是附加在URL后面,用于向服务器传递额外信息的键值对。
import requests
# 模拟在搜索引擎中搜索"Python requests"
params = {
'q': 'Python requests',
'page': 1,
'lang': 'en'
}
# requests会自动将参数编码并添加到URL后面
# 实际请求的URL会是: https://httpbin.org/get?q=Python+requests&page=1&lang=en
response = requests.get('https://httpbin.org/get', params=params)
# 查看实际请求的URL
print("实际请求的URL:", response.url)
print("状态码:", response.status_code)
示例3:发送POST请求
POST请求通常用于向服务器提交数据,例如提交表单、创建新资源。
import requests
# 1. 发送一个表单格式的POST请求 (Content-Type: application/x-www-form-urlencoded)
form_data = {
'username': 'testuser',
'password': 's3cr3t'
}
response1 = requests.post('https://httpbin.org/post', data=form_data)
print("表单POST响应:", response1.status_code)
# 2. 发送一个JSON格式的POST请求 (更常用)
json_data = {
'name': 'Alice',
'age': 30,
'hobbies': ['reading', 'coding']
}
# 使用 `json` 参数,requests会自动设置正确的Content-Type头并序列化数据
response2 = requests.post('https://httpbin.org/post', json=json_data)
print("JSON POST响应:", response2.status_code)
print("服务器收到的JSON:", response2.json()['json']) # httpbin会回显它收到的数据
示例4:设置自定义请求头
有时需要设置User-Agent(标识你的程序身份)或Authorization(携带认证令牌)等自定义请求头。
import requests
headers = {
'User-Agent': 'My Python Learning Bot 1.0',
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN' # 模拟认证令牌
}
response = requests.get('https://httpbin.org/headers', headers=headers)
print("服务器收到的请求头:")
print(json.dumps(response.json(), indent=2))
示例5:处理其他HTTP方法与异常
import requests
from requests.exceptions import HTTPError, Timeout, ConnectionError
# 常用方法:PUT (更新), DELETE (删除), PATCH (部分更新)
# response = requests.put('https://httpbin.org/put', data={'key': 'value'})
# response = requests.delete('https://httpbin.org/delete')
# 错误处理示例
try:
# 设置超时时间(秒),防止程序长时间卡住
response = requests.get('https://httpbin.org/delay/3', timeout=2)
response.raise_for_status() # 如果状态码不是2xx,抛出HTTPError异常
print("请求成功!")
except Timeout:
print("错误:请求超时。服务器在指定时间内未响应。")
except ConnectionError:
print("错误:网络连接问题。请检查你的网络。")
except HTTPError as e:
print(f"错误:HTTP错误 {e.response.status_code}")
except requests.exceptions.RequestException as e:
# 捕获所有requests可能抛出的异常的基类
print(f"发生未知请求错误: {e}")
实践练习
练习1:获取并展示GitHub用户信息
要求:
- 使用
requests库向https://api.github.com/users/{username}发送GET请求(将{username}替换为一个真实的GitHub用户名,如torvalds)。 - 解析返回的JSON数据。
- 打印出该用户的
name、bio、public_repos数量。 - 处理可能出现的用户不存在(404状态码)或网络错误。
预期输出(示例,具体数据随用户名变化):
用户名: torvalds
姓名: Linus Torvalds
简介: Software engineer, creator of Linux and Git.
公开仓库数: 8
练习2:模拟登录(POST请求)
要求:
- 使用
requests库向https://httpbin.org/post发送一个POST请求。 - 请求体为一个JSON对象,包含
"username"和"password"两个字段(值自定义)。 - 打印出服务器返回的状态码,以及服务器在
form或json字段中回显的你发送的用户名。
预期输出(示例):
状态码: 200
服务器回显的用户名 (在json中): testuser
练习3:综合练习 - 自定义请求与数据提取
要求:
- 向
https://httpbin.org/headers发送一个GET请求。 - 自定义请求头,添加一个键为
X-My-Custom-Header,值为你自己名字的头。 - 解析响应JSON,打印出服务器认为你发送的所有请求头。
- 特别地,打印出你自定义的头是否被服务器正确接收。
预期输出(示例):
服务器收到的所有请求头:
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.31.0",
"X-Amzn-Trace-Id": "Root=1-6518f1c3-...",
"X-My-Custom-Header": "张三" // 你的自定义头
}
}
我的自定义头 'X-My-Custom-Header' 被成功接收!
常见错误
-
忘记检查状态码:只获取
response而不检查response.status_code或调用response.raise_for_status(),可能对失败的请求进行后续操作,导致程序出错。# 错误示范 response = requests.get(url) data = response.json() # 如果请求失败(如404),这行会抛出异常 # 正确做法 response = requests.get(url) response.raise_for_status() # 如果状态码不是2xx,则抛出异常 # 或者手动检查 if response.status_code == 200: data = response.json() else: print(f"请求失败,状态码:{response.status_code}") -
混淆
data和json参数:发送POST请求时,data参数用于发送表单数据,json参数用于发送JSON数据。用错会导致服务器解析失败。# 发送JSON数据,应使用json参数 requests.post(url, json={'key': 'value'}) # 正确 requests.post(url, data={'key': 'value'}) # 错误,这会发送表单数据 -
未处理编码问题:使用
response.text获取文本时,requests会根据响应头猜测编码。对于非UTF-8编码的页面,可能出现乱码。可以使用response.encoding属性查看和设置。# 指定编码 response.encoding = 'gbk' # 如果已知页面是GBK编码 print(response.text) # 或者直接使用二进制内容,自行解码 print(response.content.decode('utf-8')) -
未设置超时:如果不设置
timeout参数,网络请求可能会无限期地等待,导致程序卡死。# 总是设置一个合理的超时时间 response = requests.get(url, timeout=5) # 5秒超时 -
硬编码敏感信息:将API密钥、密码等敏感信息直接写在代码中(如示例4的
Authorization头)。应使用环境变量或配置文件来管理。
小结
- HTTP基础:我们通过
requests库,用Python代码实现了与浏览器类似的功能——向服务器发送请求(GET/POST等)并接收响应。这是爬虫、调用API、自动化脚本的基础。 requests库优势:它隐藏了底层复杂性,提供了requests.get(),.post(),.json(),.status_code等直观的方法和属性,让你专注于业务逻辑。- 关键操作:
- 获取数据:
requests.get(url, params=...) - 提交数据:
requests.post(url, data=..., json=...) - 设置头部:
requests.get(url, headers=...) - 处理响应:
response.status_code,.headers,.text,.json()
- 获取数据:
- 重要习惯:始终检查状态码、处理可能的异常、设置超时、安全地管理敏感信息。
- 下一步:学会发送请求是第一步,下一课我们将学习如何使用
requests库调用符合RESTful设计规范的API,并解析更复杂的JSON数据。