Python HTTP 请求入门
一、程序怎么和外界通信
你的 Python 脚本 ──HTTP 请求──▶ 远程服务器(API) ◀──HTTP 响应── 返回 JSON / 文本二、requests 入门
安装 requests
requests 是 Python 最常用的 HTTP 库,语法简单
项目根目录下,先激活虚拟环境,再安装:
cd D:\test\python_study python -m venv .venv .venv\Scripts\activate pip install requests验证:
python-c"import requests; print(requests.__version__)"第 1 行:python -m venv .venv
作用:创建虚拟环境
| 部分 | 含义 |
|---|---|
| python | 调用系统里的 Python |
| -m venv | 以模块方式运行 venv(Python 自带的虚拟环境工具) |
| .venv | 在当前目录下创建名为 .venv 的文件夹 |
执行后会在项目根目录生成 .venv/,里面有独立的 Python 和 pip,依赖只装在这个项目里,不影响系统全局 Python。
类比前端: 类似在项目里初始化一个独立的依赖空间(类似有独立 node_modules 的项目环境),而不是用全局安装的包。
第 2 行:.venv\Scripts\activate
作用:激活虚拟环境
执行后,当前终端会「切换」到 .venv 里的 Python 和 pip。成功后提示符前会出现 (.venv)`,例如:
(.venv) PS D:\test\python_study>第 3 行:pip install requests
作用:在当前环境里安装 requests 这个第三方包
| 部分 | 含义 |
|---|---|
| pip | Python 的包管理器(类似前端的 npm / pnpm) |
| install | 安装依赖(类似 npm install) |
| requests | 要安装的包名,用来发 HTTP 请求 |
前提是你已经激活了虚拟环境(提示符里有 (.venv)),这样包装进项目的 .venv,不会污染全局 Python。
GET 请求的两种传参
| 类型 | 写法 | 例子 |
|---|---|---|
| 路径参数 | 写在 URL 路径里 | /posts/1、/api/voi/123 |
| 查询参数 | 写在?后面(Query String) | /posts?userId=1 |
前端路由里的params(如/api/voi/:id)指的是路径参数;Pythonrequests里的params=指的是查询参数,不要混用。
路径参数:直接拼进 URL
post_id=1response=requests.get(f"https://jsonplaceholder.typicode.com/posts/{post_id}")data=response.json()# 返回 dict,id=1 的那篇文章查询参数:URL 后面加?key=value
https://jsonplaceholder.typicode.com/posts?userId=1用params=传查询参数,更清晰:
response=requests.get("https://jsonplaceholder.typicode.com/posts",params={"userId":1},)print(response.url)# 看最终拼好的 URLdata=response.json()# 返回 list,该用户的所有帖子推荐的公开测试 API
本课用 JSONPlaceholder — 免费、稳定、无需 API Key:
| 端点 | 说明 |
|---|---|
| GET /posts/1 | 获取 id=1 的文章(返回 dict) |
| GET /posts?userId=1 | 获取 userId=1 的所有文章(返回 list) |
| GET /users/1 | 获取 id=1 的用户信息 |
常见错误
| 错误 | 原因 |
|---|---|
| ModuleNotFoundError: No module named ‘requests’ | 没安装或虚拟环境没激活 |
| ConnectionError | 网络不通或 URL 写错 |
| KeyError | JSON 字段名写错,先用 print(data) 看结构 |
| 不检查 status_code 直接用 .json() | 404/500 时可能解析失败 |
三、POST 请求
发 POST 的几种方式
# 方式 1:发 JSON(推荐,API 最常用)# json= 会自动做两件事:把 dict 转成 JSON 字符串,并设置 Content-Type: application/json。requests.post(url,json={"key":"value"})# 方式 2:手动发 JSON 字符串(需自己设 Content-Type)# 用 data= 传字符串时,requests 默认是 application/x-www-form-urlencoded(表单编码),需手动设为 JSON。requests.post(url,data='{"key": "value"}',headers={"Content-Type":"application/json"})# 方式 3:纯文本requests.post(url,data='hello world',headers={"Content-Type":"text/plain"})# 方式 4:表单字段(data 传 dict 时常见)requests.post(url,data={"key":"value"})# 默认 application/x-www-form-urlencodedHeader(请求头 / 响应头)
Header 是请求的「元信息」,键值对形式。
常见请求头
| Header | 作用 | 例子 |
|---|---|---|
| Content-Type | Body 是什么格式 | application/json |
| Authorization | 身份认证 | Bearer sk-xxxx(LLM API 常用) |
| User-Agent | 客户端是谁 | MyApp/1.0 |
在 requests 里设置 Header
headers={"User-Agent":"PythonStudy/1.0"}response=requests.get(url,headers=headers)看响应头
print(response.headers)# 全部响应头print(response.headers.get("Content-Type"))# 常见:application/json; charset=utf-8HTTP 状态码
| 状态码 | 含义 | 你该怎么想 |
|---|---|---|
| 200 | OK,成功 | 正常处理 response.json() |
| 201 | Created,创建成功 | POST 创建资源时常见 |
| 400 | Bad Request,请求有误 | 检查 Body、参数格式 |
| 401 | Unauthorized,未授权 | 缺 API Key 或 Key 无效 |
| 404 | Not Found,找不到 | URL 或 id 写错(你测过 99990) |
| 500 | Internal Server Error | 服务器内部错误 |
通过response.status_code读取。
读响应体
response.text# 字符串(原始文本)response.json()# 解析成 dict/list(前提是 JSON)response.content# 二进制 bytes(图片等)对象属性 和 字典键 的区别
response ← httpx.Response 对象 ├── status_code ← 对象的属性(int) ├── headers ← 对象的属性(Headers 映射) │ └── "Content-Type" ← headers 里的一个键(字符串) └── ...四、httpx — API(支持 async/await:)
安装 httpx
环境同上,pip install httpx
保存二进制文件
文本用 write_text(),图片用 write_bytes():
frompathlibimportPath path=Path("downloads/cat.jpg")path.parent.mkdir(parents=True,exist_ok=True)# 确保目录存在path.write_bytes(response.content)# 写入二进制parents=True
自动创建路径上所有缺失的上级目录。
比如:会依次创建:a → a/b → a/b/c。
如果 parents=False(默认),中间缺一层就会报错。
exist_ok=True
目录已经存在时不报错。
exist_ok=False(默认):目录已存在 → 抛 FileExistsError
exist_ok=True:目录已存在 → 静默跳过
常见组合就是「缺啥建啥,有了也不报错」。
httpx 异步下载一张图
importhttpxasyncdefdownload_image(url:str,save_path:Path)->bool:asyncwithhttpx.AsyncClient(timeout=30.0)asclient:response=awaitclient.get(url)ifresponse.status_code==200:save_path.write_bytes(response.content)returnTruereturnFalseresponse.content 是 bytes 类型,正好对应图片二进制数据。