首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python API 优化,缓存API,解决request重复请求!

Python API 优化,缓存API,解决request重复请求!

原创
作者头像
小白的大数据之旅
发布2025-12-05 15:06:36
发布2025-12-05 15:06:36
2020
举报

Python API 优化:用缓存解决重复请求,省流又提速!

咱们做开发的,肯定都遇到过这种糟心事儿:同一个 API 调用好几次,每次都等半天,还白白浪费流量 —— 要是调用的是按次收费的 API,那更是心疼钱!其实解决这问题特简单,给 API 加个 “缓存” 就行。说白了,就是第一次请求 API 的时候,把返回的数据存到本地文件里;下次再要同样的数据,直接读本地文件,不用再发网络请求了。

这篇文章就手把手教你怎么实现 API 缓存,从方案选择到实战代码,再到常见坑和面试考点,全给你讲透。哪怕你是刚接触 API 的新手,跟着做也能搞定。

一、先搞懂:为什么要做 API 缓存?缓存方案怎么选?

在写代码之前,得先明白 “为什么” 和 “选什么”,不然写出来的东西可能不适用自己的场景。

1. 缓存的核心价值:解决 3 个痛点

  • 省时间:网络请求要等服务器响应、数据传输,而读本地文件毫秒级就能完成,速度差 100 倍都不止。
  • 省流量 / 省钱:重复请求会浪费网络带宽,要是调用的是第三方付费 API(比如天气、地图 API),重复请求就是直接烧钱。
  • 减压力:减少对 API 服务器的请求次数,避免因为请求太频繁被封 IP(很多 API 有调用频率限制)。

2. 3 种常见缓存方案对比:选最适合你的

不是所有缓存都一样,不同方案适合不同场景。咱们用表格对比一下,新手优先选 “JSON 文件缓存”,简单易上手,不用额外装软件。

缓存方案

核心原理

优点

缺点

适用场景

JSON 文件缓存

把 API 数据存成本地 JSON 文件

  1. 不用装额外软件;2. 数据可视化(能直接打开看);3. 代码简单
  1. 不适合高并发;2. 多程序共享麻烦

个人项目、小脚本、数据更新不频繁的场景

Redis 缓存

把数据存在内存数据库里

  1. 速度极快(内存读写);2. 支持高并发;3. 能设置过期时间
  1. 需要额外安装 Redis 服务;2. 有学习成本

企业项目、高并发场景、多服务共享数据

Pickle 文件缓存

把 Python 对象直接序列化存文件

  1. 支持任何 Python 对象(比如列表、字典);2. 读写方便
  1. 数据不可视(打开是乱码);2. 跨语言不兼容

只在 Python 内部用、存复杂对象的场景

新手建议:先从 “JSON 文件缓存” 入手,本文也以这个方案为核心实战 —— 学会了这个,再学 Redis 也会更容易。

二、实战:用 JSON 缓存调用dummyjson.com API

咱们拿免费的 dummyjson.com 做例子(这个 API 专门用来测试,不用申请密钥,直接就能用)。目标是实现:

  1. 第一次调用 API 时,发网络请求,把数据存成 JSON 缓存文件;
  2. 后续调用时,先检查有没有缓存、缓存过没过期;
  3. 没过期就直接读缓存,过期了就重新请求并更新缓存。

1. 先准备:安装依赖库

咱们需要 requests 库来发 API 请求(Python 自带的库不好用),直接用 pip 装:

打开命令行,输入下面的命令,按回车:

代码语言:python
复制
pip install requests

2. 第一步:写个 “没缓存” 的 API 调用,感受下痛点

先写个最基础的代码,不做任何缓存,看看重复调用的耗时 —— 这样才能对比出缓存的好处。

代码语言:python
复制
import requests

import time

def get_api_data_no_cache(url):

   """没缓存的API请求函数"""

   # 记录开始时间(用来算耗时)

   start_time = time.time()

   try:

       # 发GET请求

       response = requests.get(url)

       # 检查请求是否成功(状态码200表示成功)

       response.raise_for_status()  # 要是状态码不对,会抛出错误

       # 把响应转成JSON格式(API返回的通常是JSON)

       data = response.json()

       # 算耗时(保留3位小数)

       cost_time = round(time.time() - start_time, 3)

       print(f"没缓存:请求API成功!耗时 {cost_time} 秒")

       return data

   except requests.exceptions.RequestException as e:

       print(f"没缓存:请求API失败!错误:{e}")

       return None

# 测试:调用dummyjson的“产品列表”API(返回10个产品数据)

api_url = "https://dummyjson.com/products?limit=10"

# 第一次调用

print("=== 第一次没缓存调用 ===")

data1 = get_api_data_no_cache(api_url)

# 等1秒,再调用一次(模拟重复请求)

time.sleep(1)

print("n=== 第二次没缓存调用 ===")

data2 = get_api_data_no_cache(api_url)

运行结果大概是这样

两次调用都要发网络请求,每次耗时 1 秒左右(具体看你网速)。

代码语言:python
复制
=== 第一次没缓存调用 ===

没缓存:请求API成功!耗时 0.862 秒

=== 第二次没缓存调用 ===

没缓存:请求API成功!耗时 0.795 秒

这就是痛点:明明要的是同样的数据,却要重复等 1 秒,完全没必要!

3. 第二步:加缓存逻辑!核心代码详解

现在给上面的代码加缓存功能,核心逻辑就 4 步:

① 确定缓存文件存在哪里、叫什么名;

② 检查缓存文件是否存在,以及过没过期;

③ 没过期就读缓存,过期 / 不存在就请求 API;

④ 请求到新数据后,把数据存成缓存文件。

完整代码如下,每一行都加了注释,保证你能看懂:

代码语言:python
复制
import requests

import os

import json

import time

import hashlib

def get_api_data_with_cache(

   url,

   cache_dir="api_cache",  # 缓存文件存在哪个文件夹里

   expire_seconds=3600     # 缓存过期时间(秒),这里设1小时

):

   """带缓存的API请求函数"""

   start_time = time.time()

   # --------------------------

   # 步骤1:生成唯一的缓存文件名

   # --------------------------

   # 问题:URL可能很长,直接当文件名会有问题(比如有特殊字符)

   # 解决:把URL转成MD5哈希值(固定32位字符串,安全又短)

   url_encoded = url.encode("utf-8")  # 转成字节串(哈希需要)

   url_hash = hashlib.md5(url_encoded).hexdigest()  # 生成MD5哈希

   cache_file = os.path.join(cache_dir, f"{url_hash}.json")  # 缓存文件路径:api_cache/xxx.json

   # --------------------------

   # 步骤2:检查缓存是否可用

   # --------------------------

   # 先判断缓存文件夹是否存在,不存在就创建

   if not os.path.exists(cache_dir):

       os.makedirs(cache_dir)

       print(f"创建缓存文件夹:{cache_dir}")

   # 检查缓存文件是否存在 + 没过期

   if os.path.exists(cache_file):

       # 获取缓存文件的修改时间(最后一次保存的时间)

       cache_modify_time = os.path.getmtime(cache_file)

       # 计算当前时间和修改时间的差(秒)

       time_diff = time.time() - cache_modify_time

       # 判断是否没过期

       if time_diff < expire_seconds:

           try:

               # 读缓存文件

               with open(cache_file, "r", encoding="utf-8") as f:

                   data = json.load(f)

               cost_time = round(time.time() - start_time, 3)

               print(f"✅ 命中缓存!耗时 {cost_time} 秒(缓存文件:{cache_file})")

               return data

           except json.JSONDecodeError:

               print(f"❌ 缓存文件损坏,重新请求API")

           except Exception as e:

               print(f"❌ 读取缓存出错:{e},重新请求API")

   # --------------------------

   # 步骤3:缓存不可用,请求API

   # --------------------------

   try:

       print(f"🔄 缓存不存在/已过期,请求API...")

       response = requests.get(url)

       response.raise_for_status()  # 检查请求是否成功

       data = response.json()

       # --------------------------

       # 步骤4:保存新数据到缓存文件

       # --------------------------

       with open(cache_file, "w", encoding="utf-8") as f:

           json.dump(data, f, ensure_ascii=False, indent=2)  # indent=2:格式化显示,方便人看

       cost_time = round(time.time() - start_time, 3)

       print(f"✅ API请求成功!耗时 {cost_time} 秒,已保存到缓存")

       return data

   except requests.exceptions.RequestException as e:

       print(f"❌ API请求失败!错误:{e}")

       return None

# --------------------------

# 测试:对比有缓存和没缓存的区别

# --------------------------

api_url = "https://dummyjson.com/products?limit=10"

# 第一次调用:没缓存,会请求API并创建缓存

print("=== 第一次带缓存调用 ===")

data1 = get_api_data_with_cache(api_url)

# 等1秒,第二次调用:有缓存,直接读

time.sleep(1)

print("n=== 第二次带缓存调用 ===")

data2 = get_api_data_with_cache(api_url)

# 等3601秒(超过1小时),第三次调用:缓存过期,重新请求(可选测试,不用等这么久)

# time.sleep(3601)

# print("n=== 第三次带缓存调用(缓存过期) ===")

# data3 = get_api_data_with_cache(api_url)

运行结果一定会让你惊喜

第一次调用要 1 秒左右,第二次调用耗时只有 0.001 秒(因为读的是本地文件)!

=== 第一次带缓存调用 ===

创建缓存文件夹:api_cache

🔄 缓存不存在/已过期,请求API...

✅ API请求成功!耗时 0.912 秒,已保存到缓存

=== 第二次带缓存调用 ===

✅ 命中缓存!耗时 0.001 秒(缓存文件:api_cache/xxx.json)

同时你会发现,项目文件夹里多了个 api_cache 文件夹,里面有个 .json 文件 —— 这就是咱们的缓存文件,打开能直接看到 API 返回的数据,特别直观。

4. 第三步:进阶优化:支持带参数的 API

很多 API 会带参数(比如 https://dummyjson.com/products?id=1 查单个产品),如果只按 URL 哈希,不同参数的请求会被当成同一个,导致缓存错乱。

咱们优化一下代码,让参数也参与缓存文件名的生成。修改后的核心代码(只改步骤 1 的文件名生成逻辑):

代码语言:python
复制
def get_api_data_with_cache(

   url,

   params=None,  # 新增:API参数(比如{"id":1, "limit":10})

   cache_dir="api_cache",

   expire_seconds=3600

):

   start_time = time.time()

   # --------------------------

   # 优化:参数也参与哈希,避免缓存错乱

   # --------------------------

   # 把URL和参数拼在一起,再转哈希

   if params is None:

       params = {}

   # 把参数转成有序的字符串(保证不同顺序的相同参数哈希一致,比如?id=1&limit=10和?limit=10&id=1)

   params_str = "&".join([f"{k}={v}" for k, v in sorted(params.items())])

   # 完整的请求标识:URL + 参数

   request_key = f"{url}?{params_str}" if params_str else url

   # 生成哈希文件名

   request_key_encoded = request_key.encode("utf-8")

   url_hash = hashlib.md5(request_key_encoded).hexdigest()

   cache_file = os.path.join(cache_dir, f"{url_hash}.json")

   # 后面的逻辑和之前一样...(检查缓存、请求API、保存缓存)

   # --------------------------

   # 步骤3:请求API时带上参数

   # --------------------------

   response = requests.get(url, params=params)  # 这里加了params=params

# 测试带参数的API

print("=== 调用带参数的API(id=1) ===")

data_id1 = get_api_data_with_cache("https://dummyjson.com/products", params={"id":1})

print("n=== 调用带参数的API(id=2) ===")

data_id2 = get_api_data_with_cache("https://dummyjson.com/products", params={"id":2})

运行结果

两个不同参数的请求会生成两个不同的缓存文件,不会错乱。比如查 id=1 的产品用一个缓存,查 id=2 的用另一个,完美解决参数问题。

三、避坑指南:6 个常见问题及解决办法

写缓存的时候,很容易踩坑,我把自己遇到过的问题整理好了,照着解决就行。

常见问题

现象描述

原因分析

解决办法

缓存文件乱码

打开 JSON 缓存文件,中文显示成 “u4e2du6587”

保存 JSON 时没加 ensure_ascii=False

写文件时用 json.dump(data, f, ensure_ascii=False, indent=2)

不同参数请求拿到相同缓存

查?id=1 和?id=2,返回的都是同一个产品数据

缓存文件名没包含参数,只哈希了基础 URL

把参数拼到 URL 里再哈希(参考进阶优化部分)

缓存文件越积越多

api_cache 文件夹里有几百个 JSON 文件

只创建缓存,没清理过期 / 无用的缓存

  1. 写个定时脚本,删除超过 30 天的缓存;2. 每次启动程序时清理一次

API 返回数据变了,缓存没更

服务器数据更新了,但程序还读旧缓存

缓存过期时间设太长,或者没主动更新缓存

  1. 缩短过期时间(比如数据 1 小时更一次,就设 3600 秒);2. 数据更新后主动删除对应缓存

缓存文件损坏导致程序崩溃

运行时报错 “json.decoder.JSONDecodeError”

保存缓存时程序意外退出(比如断电、强制关闭)

读缓存时加 try-except json.JSONDecodeError,捕获错误后重新请求

缓存文件夹权限不够

报错 “PermissionError: Errno 13 Permission denied”

程序没有创建 / 写入文件夹的权限

  1. 把缓存文件夹改到桌面(权限宽松);2. 用管理员身份运行程序

四、面试考点:关于 API 缓存的 5 个高频问题

如果你面试 Python 开发岗,涉及 API 优化的话,这些问题很可能会被问到。答案我都按 “大白话 + 实战经验” 的风格写好了,直接背也能用。

1. 问题:什么场景下需要给 API 加缓存?什么场景不适合加?

回答

适合加缓存的场景:

  • API 数据更新不频繁(比如产品列表、地区信息,一天更一次);
  • 同一数据被频繁调用(比如首页展示的热门数据,每秒都有人看);
  • 调用 API 有成本(比如付费 API、调用次数限制)。

不适合加缓存的场景:

  • 数据实时性要求高(比如股票价格、实时聊天消息,每秒都在变);
  • 每次请求返回的数据都不一样(比如获取当前登录用户的验证码);
  • 数据量极大(比如一次返回 1GB 的文件,存本地占空间,读起来也慢)。

2. 问题:你用 JSON 文件做缓存,遇到过什么问题?怎么解决的?

回答

遇到过 3 个主要问题,都是实战中踩过的坑:

  • 第一个是 “参数不同但缓存相同”,比如查不同产品 ID 却拿到同一个数据,后来把参数拼到 URL 里一起哈希,生成唯一的缓存文件名就解决了;
  • 第二个是 “缓存文件损坏”,比如保存时程序崩了,下次读的时候报 JSON 错误,后来加了 try-except 捕获错误,一旦出错就重新请求 API;
  • 第三个是 “缓存堆积”,文件夹里存了几百个旧缓存,后来写了个小脚本,每次启动程序就删除超过 30 天的缓存文件。

3. 问题:JSON 文件缓存和 Redis 缓存有什么区别?怎么选?

回答

最核心的区别是 “存储位置” 和 “性能”:

  • JSON 文件缓存存在硬盘上,读的时候要从硬盘读,速度慢(毫秒级),但不用装额外软件,适合个人项目、小脚本;
  • Redis 缓存存在内存里,读的时候直接从内存读,速度极快(微秒级),还支持高并发,但需要单独装 Redis 服务,适合公司的项目、高流量场景。

怎么选:

  • 个人开发、小工具:优先用 JSON 文件缓存,简单快上手;
  • 公司项目、用户量多(比如每天 10 万 + 请求):用 Redis 缓存,能扛住高并发,还能多服务共享数据(比如后端有 3 台服务器,都能读同一个 Redis 缓存)。

4. 问题:缓存有个 “缓存一致性” 问题,你怎么理解?怎么解决?

回答

“缓存一致性” 就是说:缓存里的数据和 API 服务器上的数据要保持一致,不能服务器数据更新了,缓存还是旧的,导致用户看到错的信息。

我常用两种解决办法:

  • 第一种是 “设置过期时间”,比如缓存 1 小时过期,过期后自动重新请求 API 更新缓存。这是最简单的办法,适合数据更新不频繁的场景,缺点是过期前会有短暂的不一致;
  • 第二种是 “主动更新缓存”,比如后台有个 “更新产品数据” 的功能,当管理员更新产品后,程序会主动删除这个产品对应的缓存文件。这样下次请求时就会重新获取新数据,适合数据更新后需要立即同步的场景。

5. 问题:如果 API 需要登录(带 Token),缓存的时候要注意什么?

回答

主要注意两点,避免安全和数据错乱问题:

  • 第一是 “缓存文件要保密”,Token 是用户的登录凭证,不能随便存在公共文件夹(比如 C 盘根目录),最好存在用户自己的目录(比如 Windows 的 “我的文档”、Mac 的 “文稿”),而且不要给其他用户读写权限;
  • 第二是 “按用户区分缓存”,比如两个用户登录后调用同一个 “获取个人信息” API,返回的数据不一样,要是用同一个缓存文件,就会拿到别人的信息。所以要给每个用户建单独的缓存文件夹(比如用用户 ID 当文件夹名),或者把用户 ID 加进缓存文件名的哈希里,保证每个用户的缓存独立。

五、总结:缓存虽小,却能大幅提升程序体验

咱们这篇文章从 “痛点” 出发,讲了缓存的价值、方案选择,然后手把手写了 JSON 缓存的实战代码,还整理了避坑指南和面试考点。其实 API 缓存的核心逻辑很简单:“能读本地就不发网络请求”,但细节上要考虑参数、过期时间、异常处理这些问题。

如果你是刚开始学,可以先把文中的代码复制到本地,改改 API 地址和参数,跑一遍看看效果;如果是做项目,根据场景选 JSON 或 Redis 缓存,遇到问题查避坑指南就行。

后续你还可以进阶学习:比如用 functools.lru_cache 做内存缓存(适合短期频繁调用的小数据),或者用 Redis 的 “发布订阅” 功能实现缓存主动更新 —— 但这些都是基于今天学的 “缓存思想”,基础打好了,学进阶内容会特别快。

最后,为了帮你更快落地,我可以帮你整理一份 《API 缓存实战代码模板》,包含带参数缓存、缓存清理、异常处理的完整代码,你以后做项目直接复制修改就能用。要不要我帮你整理一下?

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Python API 优化:用缓存解决重复请求,省流又提速!
    • 一、先搞懂:为什么要做 API 缓存?缓存方案怎么选?
      • 1. 缓存的核心价值:解决 3 个痛点
      • 2. 3 种常见缓存方案对比:选最适合你的
    • 二、实战:用 JSON 缓存调用dummyjson.com API
      • 1. 先准备:安装依赖库
      • 2. 第一步:写个 “没缓存” 的 API 调用,感受下痛点
      • 3. 第二步:加缓存逻辑!核心代码详解
      • 4. 第三步:进阶优化:支持带参数的 API
    • 三、避坑指南:6 个常见问题及解决办法
    • 四、面试考点:关于 API 缓存的 5 个高频问题
      • 1. 问题:什么场景下需要给 API 加缓存?什么场景不适合加?
      • 2. 问题:你用 JSON 文件做缓存,遇到过什么问题?怎么解决的?
      • 3. 问题:JSON 文件缓存和 Redis 缓存有什么区别?怎么选?
      • 4. 问题:缓存有个 “缓存一致性” 问题,你怎么理解?怎么解决?
      • 5. 问题:如果 API 需要登录(带 Token),缓存的时候要注意什么?
    • 五、总结:缓存虽小,却能大幅提升程序体验
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档