—— 连接世界的桥梁
import requests
# RESTful 风格
# GET /api/users 获取列表
# GET /api/users/123 获取详情
# POST /api/users 创建
# PUT /api/users/123 全量更新
# PATCH /api/users/123 部分更新
# DELETE /api/users/123 删除
# 获取列表
resp = requests.get("https://httpbin.org/get")
assert resp.status_code == 200
# 查看响应信息
print(resp.status_code) # 200
print(resp.headers) # 响应头
print(resp.json()) # 解析JSON
print(resp.elapsed) # 请求耗时
print(resp.encoding) # 编码
import requests
# 1. API Key(查询参数)
resp = requests.get("https://api.example.com/data",
params={"api_key": "your_key"})
# 2. Bearer Token(请求头)
headers = {"Authorization": "Bearer your_token"}
resp = requests.get("https://api.example.com/data", headers=headers)
# 3. Basic Auth
from requests.auth import HTTPBasicAuth
resp = requests.get("https://api.example.com/data",
auth=HTTPBasicAuth("username", "password"))
# 或简写
resp = requests.get("https://api.example.com/data",
auth=("username", "password"))
# 4. OAuth2(使用 requests-oauthlib)
# pip install requests-oauthlib
# from requests_oauthlib import OAuth2Session
import requests
from typing import Generator
# 方式1: 页码分页
def fetch_all_pages(base_url: str, per_page: int = 100) -> Generator[list, None, None]:
"""获取所有分页数据"""
page = 1
while True:
resp = requests.get(base_url, params={
"page": page,
"per_page": per_page,
}, timeout=10)
resp.raise_for_status()
data = resp.json()
items = data.get("items", data.get("data", []))
if not items:
break
yield items
page += 1
# 方式2: 游标/offset分页
def fetch_by_cursor(base_url: str) -> Generator[list, None, None]:
"""基于游标的分页"""
cursor = None
while True:
params = {}
if cursor:
params["cursor"] = cursor
resp = requests.get(base_url, params=params, timeout=10)
data = resp.json()
yield data.get("items", [])
cursor = data.get("next_cursor")
if not cursor:
break
# 使用:收集所有数据
# all_items = []
# for page_items in fetch_all_pages("https://api.example.com/users"):
# all_items.extend(page_items)
import requests
from typing import Any, Optional
import time
class APIClient:
"""通用 API 客户端封装"""
def __init__(self, base_url: str, token: str = "",
timeout: int = 10, max_retries: int = 3):
self.base_url = base_url.rstrip("/")
self.timeout = timeout
self.max_retries = max_retries
self.session = requests.Session()
if token:
self.session.headers["Authorization"] = f"Bearer {token}"
self.session.headers["Content-Type"] = "application/json"
def _request(self, method: str, path: str,
params: dict = None, data: dict = None) -> Any:
url = f"{self.base_url}/{path.lstrip('/')}"
for attempt in range(1, self.max_retries + 1):
try:
resp = self.session.request(
method, url, params=params, json=data,
timeout=self.timeout,
)
resp.raise_for_status()
return resp.json()
except requests.exceptions.RequestException as e:
if attempt == self.max_retries:
raise
wait = 2 ** attempt
print(f"请求失败({attempt}/{self.max_retries}): {e}, {wait}s后重试")
time.sleep(wait)
def get(self, path: str, params: dict = None) -> Any:
return self._request("GET", path, params=params)
def post(self, path: str, data: dict = None) -> Any:
return self._request("POST", path, data=data)
def put(self, path: str, data: dict = None) -> Any:
return self._request("PUT", path, data=data)
def delete(self, path: str) -> Any:
return self._request("DELETE", path)
# 使用
# api = APIClient("https://api.example.com", token="your_token")
# users = api.get("/users", params={"page": 1})
# result = api.post("/users", data={"name": "张三"})
import requests
class WeatherAPI:
"""Open-Meteo 天气 API(免费,无需key)"""
BASE_URL = "https://api.open-meteo.com/v1"
def __init__(self):
self.session = requests.Session()
def get_current(self, latitude: float, longitude: float) -> dict:
"""获取当前天气"""
params = {
"latitude": latitude,
"longitude": longitude,
"current_weather": True,
"timezone": "auto",
}
resp = self.session.get(
f"{self.BASE_URL}/forecast",
params=params, timeout=10,
)
resp.raise_for_status()
return resp.json()["current_weather"]
def get_forecast(self, latitude: float, longitude: float,
days: int = 7) -> dict:
"""获取天气预报"""
params = {
"latitude": latitude,
"longitude": longitude,
"daily": "temperature_2m_max,temperature_2m_min,precipitation_sum",
"forecast_days": days,
"timezone": "auto",
}
resp = self.session.get(
f"{self.BASE_URL}/forecast",
params=params, timeout=10,
)
resp.raise_for_status()
return resp.json()["daily"]
# 使用
# weather = WeatherAPI()
# beijing = weather.get_current(39.9, 116.4)
# print(f"北京当前温度: {beijing['temperature']}°C")
# forecast = weather.get_forecast(39.9, 116.4)
# for date, hi, lo in zip(forecast['time'], forecast['temperature_2m_max'], forecast['temperature_2m_min']):
# print(f"{date}: {lo}°C ~ {hi}°C")
#!/usr/bin/env python3
"""第16课 API调用验证"""
import requests
def test_httpbin_get():
"""GET API测试"""
resp = requests.get("https://httpbin.org/get",
params={"foo": "bar"}, timeout=10)
assert resp.status_code == 200
data = resp.json()
assert data["args"]["foo"] == "bar"
print("✅ GET API测试通过")
def test_httpbin_post():
"""POST API测试"""
resp = requests.post("https://httpbin.org/post",
json={"name": "test"}, timeout=10)
assert resp.status_code == 200
data = resp.json()
assert data["json"]["name"] == "test"
print("✅ POST API测试通过")
def test_httpbin_auth():
"""认证测试"""
resp = requests.get("https://httpbin.org/basic-auth/user/pass",
auth=("user", "pass"), timeout=10)
assert resp.status_code == 200
data = resp.json()
assert data["authenticated"] == True
print("✅ 认证测试通过")
def test_httpbin_status():
"""状态码测试"""
resp = requests.get("https://httpbin.org/status/404", timeout=10)
assert resp.status_code == 404
print("✅ 状态码测试通过")
def test_api_client():
"""API客户端封装测试"""
class SimpleClient:
def __init__(self):
self.session = requests.Session()
def get(self, path, **kw):
resp = self.session.get(f"https://httpbin.org{path}", timeout=10, **kw)
resp.raise_for_status()
return resp.json()
client = SimpleClient()
data = client.get("/get", params={"test": "1"})
assert data["args"]["test"] == "1"
print("✅ API客户端封装测试通过")
if __name__ == "__main__":
test_httpbin_get()
test_httpbin_post()
test_httpbin_auth()
test_httpbin_status()
test_api_client()
print("\n🎉 第16课全部验证通过!")