Python总结----Requests库深度理解(四)

第一章:GET()请求

 代码示例:

 1 import requests
 2 from requests.exceptions import RequestException
 3 
 4 def get_request(url, params=None, headers=None):
 5     try:
 6         response = requests.get(
 7             url,
 8             params=params,
 9             headers=headers,
10             timeout=5  # 设置超时时间
11         )
12         
13         # 抛出HTTP错误(如404, 500等)
14         response.raise_for_status()
15         
16         # 尝试解析JSON
17         try:
18             data = response.json()
19             return {"status": "success", "data": data}
20         except ValueError:
21             return {"status": "success", "data": response.text}
22             
23     except RequestException as e:
24         return {"status": "error", "message": str(e)}
25 
26 # 使用示例
27 result = get_request(
28     url="https://api.example.com/data",
29     params={"id": 123},
30     headers={"Accept": "application/json"}
31 )
32 
33 if result["status"] == "success":
34     print("获取的数据:", result["data"])
35 else:
36     print("请求出错:", result["message"])

response.raise_for_status()是 requests 库中的一个重要方法,它的作用是检查 HTTP 请求是否成功,如果请求返回了错误状态码(即非 2xx 的状态码,如 404、500 等),它会抛出一个 HTTPError 异常。如果响应状态码是 200201204 等(即 2xx 成功状态码),raise_for_status() 不会执行任何操作,程序继续执行。如果状态码是 4xx(客户端错误,如 404 Not Found)或 5xx(服务器错误,如 500 Internal Server Error),它会抛出 requests.exceptions.HTTPError 异常。

response.json():将 HTTP 响应体(response.text)解析为 Python 字典或列表(取决于 JSON 数据结构)。如果响应内容不是有效的 JSON,会抛出 ValueError 异常。response.text:返回响应体的原始字符串(如 '{"name": "Alice", "age": 25}')。response.json() 内部调用 json.loads(response.text),将字符串解析为 Python 对象

 

 

第二章:POST()请求

HTTP规定POST提交的数据必须放在消息主体中,但是协议并没有规定必须使用何种编码方式。服务端通常是根据请求头中的Content-Type字段来获知请求中的消息主体是用何种方式进行编码。再对消息主体进行解析。常见的几种编码方式:

  • application/x-www-form-urlencoded:以Form表单形式提交数据,提交的数据按照key1=1&key2=2的方式进行编码
  • application/json:以JSON字符串方式提交数据;
  • text/xml:以XML文档字符串方式提交数据
  • multipart/form-data:一般用来上传文件

application/json(JSON 格式)代码示例:

 1 import requests
 2 import json
 3 
 4 url = "https://api.example.com/post"
 5 data = {"name": "Alice", "age": 25}  # Python 字典
 6 headers = {"Content-Type": "application/json"}
 7 
 8 # 方法1:直接传递 dict,requests 会自动序列化为 JSON
 9 response = requests.post(url, json=data, headers=headers)
10 
11 # 方法2:手动序列化(效果相同)
12 response = requests.post(
13     url,
14     data=json.dumps(data),  # 手动转为 JSON 字符串
15     headers=headers
16 )
17 
18 print(response.json())  # 解析响应 JSON

text/xml(XML 格式)代码示例:

 1 import requests
 2 
 3 url = "https://api.example.com/xml"
 4 xml_data = """
 5 <user>
 6     <name>Alice</name>
 7     <age>25</age>
 8 </user>
 9 """
10 headers = {"Content-Type": "text/xml"}
11 
12 response = requests.post(url, data=xml_data, headers=headers)
13 print(response.text)  # 响应可能是 XML 或 JSON

multipart/form-data(表单上传文件)代码示例:

1 # 纯上传文件
2 import requests
3 
4 url = "https://api.example.com/upload"
5 files = {"file": open("example.txt", "rb")}  # 以二进制模式打开文件
6 
7 response = requests.post(url, files=files)
8 print(response.json())
1 # 表单提交+文件
2 import requests
3 
4 url = "https://api.example.com/submit"
5 data = {"username": "alice"}
6 files = {"avatar": open("avatar.jpg", "rb")}
7 
8 response = requests.post(url, data=data, files=files)
9 print(response.json())
1 # 上传多个文件
2 files = [
3     ('files', ('file1.txt', open('file1.txt', 'rb'))),
4     ('files', ('file2.jpg', open('file2.jpg', 'rb'), 'image/jpeg'))
5 ]
6 response = requests.post(url, files=files)

换种方法实现文件上传

 1 import requests
 2 
 3 url = "http://httpbin.org/post"  # 测试用的上传接口
 4 file_path = "example.txt"       # 本地文件路径
 5 
 6 # 方法1:直接打开文件
 7 with open(file_path, 'rb') as f:
 8     response = requests.post(url, files={'file': f})
 9 
10 # 方法2:指定文件名和内容(无需本地文件)
11 response = requests.post(url, files={'file': ('custom_name.txt', b'file content')})
12 
13 print(response.json())  # 查看服务器返回的响应

 

application/x-www-form-urlencoded(默认表单格式)代码示例:

1 import requests
2 
3 url = "https://api.example.com/form"
4 data = {"name": "Alice", "age": 25}
5 headers = {"Content-Type": "application/x-www-form-urlencoded"}
6 
7 response = requests.post(url, data=data)  # 默认就是这种格式,可省略 headers
8 print(response.json())

总结:

Content-Type 用途 requests 参数
application/json JSON 数据 json=data 或 data=json.dumps(data)
text/xml XML数据 data=xml_string
multipart/form-data
文件上传
files={"file": open("file.txt", "rb")}
application/x-www-form-urlencoded 表单数据 data={"key": "value"}(默认)
text/plain 纯文本 data="raw text"

 

 

 

第三章:处理TOKEN

登录接口请求后提Token,随后在下个请求中使用Token值,示例代码如下:

 1 import requests
 2 import pytest
 3 import os
 4 
 5 class ApiClient:
 6     """API 客户端封装类"""
 7     BASE_URL = "http://www.example.com"
 8     LOGIN_URL = f"{BASE_URL}/login"
 9     TASKS_URL = f"{BASE_URL}/api/tasks"
10     TOKEN_FILE = "token.md"
11 
12     @classmethod
13     def login(cls, username: str = "LZL", password: str = "123") -> str:
14         """登录并获取 Token"""
15         response = requests.post(
16             url=cls.LOGIN_URL,
17             json={"username": username, "password": password},
18             headers={"Content-Type": "application/json"}
19         )
20         response.raise_for_status()
21         token = response.json().get('token')
22         if not token:
23             raise ValueError("Token not found in response!")
24         return token
25 
26     @classmethod
27     def save_token(cls, token: str):
28         """将 Token 保存到文件"""
29         with open(cls.TOKEN_FILE, 'w') as f:
30             f.write(token)
31 
32     @classmethod
33     def load_token(cls) -> str:
34         """从文件加载 Token"""
35         if not os.path.exists(cls.TOKEN_FILE):
36             raise FileNotFoundError(f"Token file '{cls.TOKEN_FILE}' not found!")
37         with open(cls.TOKEN_FILE, 'r') as f:
38             return f.read().strip()
39 
40     @classmethod
41     def get_tasks(cls, token: str) -> dict:
42         """获取任务列表"""
43         headers = {"Authorization": f"Bearer {token}"}
44         response = requests.get(url=cls.TASKS_URL, headers=headers)
45         response.raise_for_status()
46         return response.json()
47 
48 # -------------------- 测试代码 --------------------
49 
50 @pytest.fixture(scope="session")
51 def token():
52     """会话级 Fixture:优先从文件加载 Token,不存在则登录获取"""
53     try:
54         token = ApiClient.load_token()
55     except FileNotFoundError:
56         token = ApiClient.login()
57         ApiClient.save_token(token)
58     return token
59 
60 def test_get_tasks(token):
61     """测试获取任务列表接口"""
62     tasks_data = ApiClient.get_tasks(token)
63     assert 'tasks' in tasks_data  # 假设返回数据包含 tasks 字段
64 
65 def test_token_file_persistence(token):
66     """测试 Token 文件读写一致性"""
67     # 重新加载 Token 并验证与当前 Token 一致
68     loaded_token = ApiClient.load_token()
69     assert loaded_token == token

 

第四章:接口测试框架涉及和开发

框架涉及如下:

新建项目名称为:testAPI,其下有6个文件夹分别是:Config、Data、Log、Reports、TestCases、Utils和__init__.py文件、runMain.py文件。

其中:

Config文件下有:__init__.py、config.ini、HTMLTestRunner.py、readConfig.py

Data文件下有:__init__.py、data.xlsx、taskID、Token_md

Log文件下有:__init__.py、log.txt

Reports下有:2025-06-26 13_12_11result.html、__init__.py

TestCases下有:__init__.py、test_tasks.py

Utils下有:__init__.py、excels.py、page.py

在testAPI/Utils中的page.py文件下编写page.py文件重构requests请求,代码如下:

  1 import requests
  2 from typing import Optional, Dict, Any, Union
  3 import logging
  4 
  5 class HttpClient:
  6     """
  7     Requests 二次封装类,支持 GET/POST 请求,具备以下功能:
  8     - 统一超时设置
  9     - 自动异常处理
 10     - 请求日志记录
 11     - 响应内容自动解析(JSON/文本)
 12     - 自定义请求头
 13     """
 14 
 15     DEFAULT_TIMEOUT = 10  # 默认超时时间(秒)
 16     DEFAULT_HEADERS = {
 17         "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
 18         "Content-Type": "application/json"
 19     }
 20 
 21     def __init__(self, base_url: Optional[str] = None):
 22         self.base_url = base_url.rstrip("/") if base_url else None
 23         self.session = requests.Session()  # 使用 Session 保持连接
 24 
 25     def _request(
 26         self,
 27         method: str,
 28         url: str,
 29         params: Optional[Dict] = None,
 30         data: Optional[Union[Dict, str]] = None,
 31         json: Optional[Dict] = None,
 32         headers: Optional[Dict] = None,
 33         timeout: Optional[Union[int, tuple]] = None,
 34         **kwargs
 35     ) -> Any:
 36         """
 37         核心请求方法(私有)
 38         :param method: HTTP 方法(GET/POST/PUT等)
 39         :param url: 请求路径(自动拼接 base_url)
 40         :param params: URL 查询参数
 41         :param data: 表单数据
 42         :param json: JSON 数据
 43         :param headers: 请求头
 44         :param timeout: 超时时间(秒)
 45         :return: 解析后的响应内容(JSON 或文本)
 46         """
 47         # 拼接完整 URL
 48         full_url = f"{self.base_url}/{url.lstrip('/')}" if self.base_url else url
 49 
 50         # 合并请求头(默认头 + 自定义头)
 51         merged_headers = {**self.DEFAULT_HEADERS, **(headers or {})}
 52 
 53         # 统一超时设置
 54         timeout = timeout or self.DEFAULT_TIMEOUT
 55 
 56         try:
 57             response = self.session.request(
 58                 method=method,
 59                 url=full_url,
 60                 params=params,
 61                 data=data,
 62                 json=json,
 63                 headers=merged_headers,
 64                 timeout=timeout,
 65                 **kwargs
 66             )
 67             response.raise_for_status()  # 自动检查 HTTP 状态码
 68 
 69             # 尝试解析 JSON,失败则返回文本
 70             try:
 71                 return response.json()
 72             except ValueError:
 73                 return response.text
 74 
 75         except requests.exceptions.RequestException as e:
 76             logging.error(f"请求失败: {method} {full_url} - {str(e)}")
 77             raise  # 重新抛出异常,由调用方处理
 78 
 79     def get(
 80         self,
 81         url: str,
 82         params: Optional[Dict] = None,
 83         headers: Optional[Dict] = None,
 84         timeout: Optional[Union[int, tuple]] = None,
 85         **kwargs
 86     ) -> Any:
 87         """
 88         GET 请求封装
 89         :param url: 请求路径
 90         :param params: URL 查询参数
 91         :param headers: 请求头
 92         :param timeout: 超时时间
 93         :return: 响应内容
 94         """
 95         return self._request(
 96             method="GET",
 97             url=url,
 98             params=params,
 99             headers=headers,
100             timeout=timeout,
101             **kwargs
102         )
103 
104     def post(
105         self,
106         url: str,
107         data: Optional[Union[Dict, str]] = None,
108         json: Optional[Dict] = None,
109         headers: Optional[Dict] = None,
110         timeout: Optional[Union[int, tuple]] = None,
111         **kwargs
112     ) -> Any:
113         """
114         POST 请求封装
115         :param url: 请求路径
116         :param data: 表单数据
117         :param json: JSON 数据
118         :param headers: 请求头
119         :param timeout: 超时时间
120         :return: 响应内容
121         """
122         return self._request(
123             method="POST",
124             url=url,
125             data=data,
126             json=json,
127             headers=headers,
128             timeout=timeout,
129             **kwargs
130         )

备注:

1、{**dict1, **dict2}: Python 的字典解包语法,将多个字典合并为一个新字典。

2、timeout = timeout or self.DEFAULT_TIMEOUT:or 的短路行为:如果 timeout 有值(非 None、非 0、非空),则直接使用 timeout如果 timeout 是 None 或其他假值(如 0),则使用 self.DEFAULT_TIMEOUT

 

posted on 2025-07-02 21:30  水中雨  阅读(37)  评论(0)    收藏  举报

导航