Python(3)--Requests库学习文档

Requests库学习文档

📚 文档概述

本文档整合了Requests库从零开始的学习内容,包括基础概念、实践代码、常见问题及解答,方便后续复习和查阅。


1. Requests库基础介绍

1.1 什么是Requests库?

import requests

# Requests是Python中最流行的HTTP库,用于发送各种HTTP请求
# 替代了Python内置的urllib,提供更简洁直观的API

1.2 与urllib对比的优势

# urllib写法(复杂)
import urllib.request
import urllib.parse
data = urllib.parse.urlencode({'key': 'value'}).encode()
req = urllib.request.Request('http://httpbin.org/post', data=data)
response = urllib.request.urlopen(req)

# Requests写法(简洁)
response = requests.post('http://httpbin.org/post', data={'key': 'value'})

# 优势总结:
# 1. 语法更简洁直观
# 2. 自动处理编码
# 3. 更好的错误处理
# 4. 更完善的功能

1.3 安装

pip install requests

2. GET请求详解

2.1 基础GET请求

import requests

# 最简单的GET请求
response = requests.get('https://jsonplaceholder.typicode.com/posts/1')
print(response.status_code)  # 状态码
print(response.text)         # 响应内容

# 为什么要检查status_code?
# 因为网络请求可能失败,需要知道请求状态

2.2 带参数的GET请求

# 方法1:直接在URL中添加参数(不推荐)
response = requests.get('https://jsonplaceholder.typicode.com/posts?userId=1')

# 方法2:使用params参数(推荐)
params = {
    'userId': 1,
    '_limit': 5
}
response = requests.get('https://jsonplaceholder.typicode.com/posts', params=params)

print(response.url)  # 查看最终请求的URL
print(response.json())  # 将响应解析为JSON

# 为什么用params而不是手动拼接URL?
# 1. 自动URL编码(处理特殊字符)
# 2. 更清晰的代码结构
# 3. 自动处理数组等复杂参数

3. POST请求详解

3.1 基础POST请求

# 表单数据POST
data = {
    'title': '测试文章',
    'body': '这是测试内容',
    'userId': 1
}
response = requests.post('https://jsonplaceholder.typicode.com/posts', data=data)

# JSON数据POST
json_data = {
    'user': {
        'name': 'Alice',
        'email': 'alice@example.com'
    }
}
response = requests.post('https://jsonplaceholder.typicode.com/posts', json=json_data)

# 为什么区分data和json参数?
# data:发送表单格式数据(application/x-www-form-urlencoded)
# json:发送JSON格式数据(application/json)
# 服务器根据Content-Type头来解析数据

3.2 文件上传

# 上传文件
files = {'file': open('example.txt', 'rb')}
response = requests.post('https://httpbin.org/post', files=files)

# 为什么用'rb'模式打开文件?
# 'rb'表示以二进制只读模式打开,确保文件内容不被修改,适合网络传输

4. Headers详解

4.1 为什么要设置Headers?

# 带自定义Headers的请求
headers = {
    'User-Agent': 'MyApp/1.0',           # 标识客户端类型
    'Authorization': 'Bearer your_token', # 身份验证
    'Content-Type': 'application/json',   # 告诉服务器数据格式
    'Accept': 'application/json'          # 告诉服务器期望的响应格式
}

response = requests.get('https://httpbin.org/user-agent', headers=headers)

# Headers的作用:
# 1. 身份验证(Authorization)
# 2. 内容协商(Accept, Content-Type)
# 3. 客户端信息(User-Agent)
# 4. 缓存控制(Cache-Control)
# 5. Cookies管理

4.2 Content-Type详解

# Content-Type = "我发给你的数据是什么格式,请你按这个格式来解析"

# 常见的Content-Type类型:
content_types = {
    'application/x-www-form-urlencoded': 'HTML表单数据',
    'application/json': 'JSON格式数据',
    'multipart/form-data': '文件上传表单',
    'text/plain': '纯文本数据',
    'application/xml': 'XML格式数据'
}

# 实践:正确设置Content-Type
def user_registration():
    """用户注册(表单数据)"""
    form_data = {
        'username': 'new_user',
        'email': 'user@example.com',
        'password': 'secure_password'
    }
    
    response = requests.post(
        'https://httpbin.org/post',
        data=form_data,
        headers={'Content-Type': 'application/x-www-form-urlencoded'}
    )
    return response

def create_product():
    """创建产品(JSON数据)"""
    product_data = {
        'product': {
            'name': '智能手机',
            'price': 2999.99,
            'specifications': {
                'screen': '6.5英寸',
                'memory': '8GB'
            }
        }
    }
    
    response = requests.post(
        'https://httpbin.org/post',
        json=product_data  # 自动设置Content-Type为application/json
    )
    return response

4.3 response.headers.get()方法详解

response = requests.get('https://jsonplaceholder.typicode.com/posts/1')

# 基本语法:response.headers.get(key, default=None)
content_type = response.headers.get('Content-Type')
print(f"Content-Type: {content_type}")

# 如果头部不存在,返回None(不会报错)
non_existent = response.headers.get('Non-Existent-Header')
print(f"不存在的头部: {non_existent}")  # 输出: None

# 可以设置默认值
non_existent_with_default = response.headers.get('Non-Existent-Header', '默认值')
print(f"带默认值的头部: {non_existent_with_default}")  # 输出: 默认值

# 为什么使用.get()而不是直接访问?
# 1. 避免KeyError异常
# 2. 代码更简洁
# 3. 可以设置默认值
# 4. 更符合Python的"请求原谅比请求许可更容易"哲学

5. 响应处理

5.1 响应头和响应体分离的原因

response = requests.get('https://jsonplaceholder.typicode.com/posts/1')

# 响应头
print("=== 响应头 ===")
for key, value in response.headers.items():
    print(f"{key}: {value}")

# 响应体  
print("\n=== 响应体 ===")
print(response.text)

# 为什么需要这种分离设计?
# 设计哲学:元数据 vs 实际数据
# 类比:响应头 = 快递单,响应体 = 快递包裹里的实际物品

5.2 响应头体的分离优势

def analyze_response(response):
    """分析响应结构"""
    print("📦 快递单信息(响应头):")
    print(f"  快递公司: {response.headers.get('Server', 'Unknown')}")
    print(f"  包裹类型: {response.headers.get('Content-Type', 'Unknown')}")
    print(f"  包裹大小: {response.headers.get('Content-Length', 'Unknown')} 字节")
    
    print("\n🎁 包裹内容(响应体):")
    print(f"  实际物品: {response.text[:100]}...")

# 分离的优势:
# 1. 性能优化:客户端可以先读取头部决定如何处理,无需解析整个响应
# 2. 功能分离:头部处理控制信息,体部承载实际数据  
# 3. 缓存效率:缓存系统只需读取头部即可决定缓存策略
# 4. 安全扫描:安全设备可以快速检查头部而不必解析内容
# 5. 协议扩展:通过添加新的头部字段来扩展功能,不影响现有数据格式

6. POST请求中表单数据和JSON数据的场景区分

6.1 基础概念对比

# 表单数据 (application/x-www-form-urlencoded)
form_data = {
    'username': 'john_doe',
    'password': 'secret123',
    'remember_me': '1'
}

# JSON数据 (application/json)  
json_data = {
    'user': {
        'username': 'john_doe',
        'profile': {
            'age': 25,
            'preferences': ['music', 'sports']
        }
    }
}

6.2 场景决策指南

def smart_data_selector(scenario_description, data_structure):
    """智能选择使用data还是json参数"""
    print(f"\n场景分析: {scenario_description}")
    
    # 分析数据结构特征
    has_nesting = any(isinstance(v, (dict, list)) for v in data_structure.values())
    has_special_types = any(isinstance(v, (bool, int, float, list, dict)) 
                           for v in data_structure.values())
    
    # 决策逻辑
    if has_nesting or has_special_types:
        recommendation = "json"
        reason = "数据结构复杂,包含嵌套或特殊类型"
    else:
        recommendation = "data" 
        reason = "简单键值对,适合表单格式"
    
    print(f"推荐使用: {recommendation}参数")
    print(f"原因: {reason}")
    return recommendation

# 测试不同场景
test_scenarios = [
    ("用户登录", {"username": "john", "password": "secret", "remember_me": True}),
    ("创建产品", {"product": {"name": "Laptop", "price": 999.99, "tags": ["tech"]}}),
    ("搜索过滤", {"category": "electronics", "price_min": 100, "price_max": 500}),
]

for scenario, data in test_scenarios:
    smart_data_selector(scenario, data)

6.3 具体行业场景

# 电子商务场景
def ecommerce_scenarios():
    """电子商务场景分析"""
    
    # 添加到购物车 → 表单数据
    add_to_cart_form = {
        'product_id': '12345',
        'quantity': '2',
        'variant': 'blue-large'
    }
    
    # 创建完整订单 → JSON数据  
    create_order_json = {
        'order': {
            'customer_id': 'cust_789',
            'items': [
                {
                    'product_id': '12345',
                    'quantity': 2,
                    'price': 29.99,
                    'options': {'color': 'blue', 'size': 'L'}
                }
            ]
        }
    }
    
    return add_to_cart_form, create_order_json

# 社交媒体场景
def social_media_scenarios():
    """社交媒体场景分析"""
    
    # 点赞操作 → 表单数据
    like_form = {
        'post_id': 'post_123',
        'action': 'like',
        'user_id': 'user_456'
    }
    
    # 发布复杂帖子 → JSON数据
    create_post_json = {
        'post': {
            'author_id': 'user_456',
            'content': {
                'text': 'Check out this amazing photo!',
                'mentions': ['@user123', '@user789'],
                'hashtags': ['#python', '#coding']
            }
        }
    }
    
    return like_form, create_post_json

6.4 技术栈决定数据格式

# 传统技术栈 → 表单数据
traditional_tech = {
    'backend': ['PHP', 'Laravel', 'Django', 'Ruby on Rails'],
    '前端': '服务器渲染模板',
    '数据格式': '表单数据 (data)',
    '示例': '用户注册、登录、搜索过滤'
}

# 现代技术栈 → JSON数据  
modern_tech = {
    'backend': ['Node.js', 'FastAPI', 'Spring Boot', 'GraphQL'],
    '前端': ['React', 'Vue', 'Angular', '移动App'],
    '数据格式': 'JSON数据 (json)', 
    '示例': 'RESTful API、微服务、移动后端'
}

7. 错误处理和最佳实践

7.1 完整的错误处理

def robust_request(url, method='GET', **kwargs):
    """
    健壮的请求函数,包含完整的错误处理
    """
    try:
        if method.upper() == 'GET':
            response = requests.get(url, **kwargs)
        elif method.upper() == 'POST':
            response = requests.post(url, **kwargs)
        else:
            raise ValueError(f"不支持的HTTP方法: {method}")
        
        # 如果状态码不是200,引发HTTPError异常
        response.raise_for_status()
        return response
        
    except requests.exceptions.HTTPError as e:
        print(f"HTTP错误: {e}")
    except requests.exceptions.ConnectionError as e:
        print(f"连接错误: {e}")
    except requests.exceptions.Timeout as e:
        print(f"超时错误: {e}")
    except requests.exceptions.RequestException as e:
        print(f"请求异常: {e}")
    except Exception as e:
        print(f"未知错误: {e}")

# 使用示例
response = robust_request(
    'https://jsonplaceholder.typicode.com/posts/1',
    timeout=5
)

7.2 API监控工具

class APIMonitor:
    def __init__(self):
        self.session = requests.Session()
    
    def check_api_health(self, url):
        """检查API健康状况,分析响应头"""
        try:
            response = self.session.get(url, timeout=10)
            
            health_info = {
                'url': url,
                'status_code': response.status_code,
                'response_time': response.elapsed.total_seconds(),
                'headers': {
                    'server': response.headers.get('Server', 'Unknown'),
                    'content_type': response.headers.get('Content-Type', 'Unknown'),
                    'content_length': response.headers.get('Content-Length', 'Unknown'),
                },
                'is_healthy': 200 <= response.status_code < 300
            }
            
            return health_info
            
        except requests.exceptions.RequestException as e:
            return {
                'url': url,
                'error': str(e),
                'is_healthy': False
            }

8. 综合实践项目

8.1 GitHub用户信息查询

def github_user_info(username):
    """
    获取GitHub用户信息的完整示例
    """
    headers = {
        'User-Agent': 'PythonLearningApp/1.0',
        'Accept': 'application/vnd.github.v3+json'
    }
    
    url = f'https://api.github.com/users/{username}'
    
    try:
        response = requests.get(url, headers=headers, timeout=10)
        
        if response.status_code == 200:
            user_data = response.json()
            print(f"用户名: {user_data['login']}")
            print(f"姓名: {user_data.get('name', '未设置')}")
            print(f"粉丝数: {user_data['followers']}")
            return user_data
            
        elif response.status_code == 404:
            print(f"错误:用户 '{username}' 不存在")
        else:
            print(f"HTTP错误: {response.status_code}")
            
    except requests.exceptions.Timeout:
        print("请求超时,请检查网络连接")
    except requests.exceptions.ConnectionError:
        print("网络连接错误")
    except requests.exceptions.RequestException as e:
        print(f"请求异常: {e}")

# 测试
github_user_info('octocat')

8.2 智能API客户端

class SmartAPIClient:
    def __init__(self, base_url):
        self.base_url = base_url
        self.session = requests.Session()
    
    def make_request(self, endpoint, method='GET', **kwargs):
        """智能API请求,充分利用头部信息"""
        url = f"{self.base_url}/{endpoint}"
        response = self.session.request(method, url, **kwargs)
        
        # 根据Content-Type自动选择解析方式
        return self._parse_response(response)
    
    def _parse_response(self, response):
        """根据Content-Type智能解析响应体"""
        content_type = response.headers.get('Content-Type', '')
        
        if 'application/json' in content_type:
            return response.json()
        elif 'text/html' in content_type or 'text/plain' in content_type:
            return response.text
        elif 'image/' in content_type:
            return response.content
        else:
            return response.content

# 使用示例
client = SmartAPIClient('https://jsonplaceholder.typicode.com')
result = client.make_request('posts/1')
print(f"解析结果: {result}")

9. 关键概念总结

9.1 GET vs POST

  • GET:获取数据,参数在URL中,用params
  • POST:提交数据,数据在请求体中,用data或json

9.2 data vs json 参数

  • data:表单格式,简单键值对
  • json:JSON格式,复杂嵌套数据结构

9.3 Headers核心作用

  • Content-Type:告诉服务器你发送的数据格式
  • Authorization:身份验证
  • User-Agent:客户端标识
  • Accept:期望的响应格式

9.4 响应处理

  • 响应头:元数据信息(状态、内容类型、缓存等)
  • 响应体:实际数据内容

10. 常见问题解答

Q: 为什么POST请求不用params,反而用data和json?

A: 这是由HTTP协议设计决定的:

  • GET请求参数在URL中,适合获取数据和简单参数
  • POST请求数据在请求体中,更适合提交复杂数据和敏感信息
  • data用于表单格式,json用于JSON格式

Q: response.headers.get()方法怎么用?

A:

# 语法:response.headers.get(key, default=None)
content_type = response.headers.get('Content-Type')
server = response.headers.get('Server', 'Unknown')  # 设置默认值

# 优势:避免KeyError,代码更健壮

Q: 表单数据和JSON数据如何选择?

A: 根据数据结构和应用场景:

  • 表单数据:简单键值对,传统Web应用
  • JSON数据:复杂嵌套结构,现代API

Q: Content-Type的作用是什么?

A: Content-Type告诉服务器"我发给你的数据是什么格式,请你按这个格式来解析"


📝 学习建议

  1. 多实践:在实际项目中应用这些概念
  2. 查看文档:遇到问题先查看官方文档
  3. 使用调试工具:利用浏览器开发者工具观察网络请求
  4. 错误处理:始终包含完整的错误处理逻辑
  5. 遵循最佳实践:根据场景选择合适的数据格式和方法

本文档根据实际学习过程整理,涵盖Requests库的核心概念和实践技巧,适合随时复习查阅。

posted @ 2025-10-21 22:12  CalvinMax  阅读(160)  评论(0)    收藏  举报