唐诗宋词元曲查询:免费也有档次限制,如何用本地缓存避免触发限流?
# 唐诗宋词元曲查询:免费也有档次限制,如何用本地缓存避免触发限流?
> 接口:唐诗宋词元曲等诗词查询(apiCode=1620)· 免费(有使用档次限制)· 请求方式 POST/GET · 返回 JSON · 适用:已接入、担心触发限流的开发者 · 阅读约 6 分钟
## 核心要点
- 接口**免费但设有使用档次(积分)限制**以防滥用,高频/重复调用会消耗档位、可能触发限流
- 诗词类数据相对稳定,最适合做本地缓存:朝代列表几乎不变,诗人/诗词可按 ID 做键缓存
- 缓存核心是「以 ID 为键、设置合理 TTL、命中即返回」,可显著降低真实调用次数(具体节省比例取决于你的访问模式,需实测)
## Why:免费不等于能随意刷
很多团队一看「免费」就放心高频调用,结果没几天发现请求被限。文档明确写着「为防止滥用设有使用档次限制」。诗词数据本身变化极慢(朝代、诗人、古诗都是相对稳定的),绝大多数请求其实是重复查询。把结果缓存在自己这边,既保护自己不被限流,也提升响应速度——这是接入免费接口的基本功。
## What:限流与缓存前提
| 项目 | 说明 |
|------|------|
| 计费 | 免费,注册默认可调用,有使用档次(积分)限制 |
| 档位说明 | 以官方免费 API 页面为准,文档未给具体数字 |
| 限流表现 | 文档未公开具体限流码;超档位时可能出现调用受限,需以 `remark` 提示为准 |
| 数据稳定性 | 朝代列表极稳定;诗人/诗词更新频率低,适合缓存 |
## How:三层缓存实践
### 步骤 1 · 设计缓存键
以接口 + 入参特征做键,例如:
- 1620-3 朝代列表:`poem:dynasties`(全局一份)
- 1620-4 诗人:`poem:poet:{dynastyId或poet}` 或 `poem:poet:id:{poetId}`
- 1620-5 诗词:`poem:poems:poet:{poetId}:page:{page}` 或 `poem:poem:id:{poemId}`
### 步骤 2 · 先查缓存,未命中再调接口
**Python(requests + 内存缓存示意,生产可换 Redis)**
```python
import requests, time
APP_KEY = "YOUR_APPKEY"
H = {"content-type": "application/x-www-form-urlencoded"}
CACHE = {} # 生产环境换成 Redis:redis.get / redis.setex
def cached_call(path, key, ttl=86400, **params):
if key in CACHE and CACHE[key][1] > time.time():
return CACHE[key][0] # 命中缓存
r = requests.post(f"https://route.showapi.com/{path}",
params={"appKey": APP_KEY, **params}, headers=H, timeout=10)
body = r.json().get("showapi_res_body", {})
if body.get("ret_code") != "0":
raise RuntimeError(body.get("remark"))
CACHE[key] = (body, time.time() + ttl) # 写入缓存,TTL 1 天
return body
# 朝代列表几乎不变,长 TTL
dyn = cached_call("1620-3", "poem:dynasties", ttl=7*86400)
# 某诗人作品,按 poetId+page 缓存
poems = cached_call("1620-5", "poem:poems:poet:5b1e3644cbf69480cb8e81b7:page:1",
poetId="5b1e3644cbf69480cb8e81b7", page=1)
```
**Redis 版关键片段(伪代码)**
```python
import redis
r = redis.Redis(host="localhost", port=6379, db=0)
def cached_call_redis(path, key, ttl, **params):
hit = r.get(key)
if hit:
return json.loads(hit) # 命中
body = real_call(path, **params)
r.setex(key, ttl, json.dumps(body)) # 写回,TTL 秒
return body
```
**Node.js(fetch + Map 缓存示意)**
```javascript
const APP_KEY = "YOUR_APPKEY";
const H = { "content-type": "application/x-www-form-urlencoded" };
const cache = new Map();
async function cachedCall(path, key, ttl, params) {
const hit = cache.get(key);
if (hit && hit.exp > Date.now()) return hit.data;
const resp = await fetch(`https://route.showapi.com/${path}?appKey=${APP_KEY}`, {
method: "POST", headers: H, body: new URLSearchParams(params),
});
const body = (await resp.json()).showapi_res_body;
if (body.ret_code !== "0") throw new Error(body.remark);
cache.set(key, { data: body, exp: Date.now() + ttl * 1000 });
return body;
}
```
### 步骤 3 · 设置差异化 TTL
- 朝代列表:7 天(几乎不变)
- 诗人 `biography`:1~7 天
- 诗词 `contentlist`:1~3 天(低频更新)
- 翻页拉全量结果:缓存整份,避免重复翻 13 页
## 返回示例与解析
缓存前后的调用次数对比(逻辑示意,非文档数字):
```
无缓存:每次访问苏轼页 → 调 1620-5 × N 次/天
加缓存:首次调 1 次写入,之后 N 次均命中本地 → 真实调用 ≈ 1 次/天
```
> 具体节省比例取决于你的访问分布(热点集中度、是否翻全量),**需结合自身流量实测**,本文不给出固定百分比。
## 进阶 / 边界
- **先缓存、后限流**:缓存能挡掉绝大多数重复请求,是比「限流算法」更优先的手段。
- **TTL 不是越长越好**:诗词极少变,但数据源仍可能修订译文/注释,设 1~7 天 TTL 并支持手动刷新即可。
- **缓存键要含全部入参**:1620-5 的 `poetId`+`page` 都要进键,否则不同页会串数据。
- **档位未知要先评估**:文档未给具体档位数字,上线前建议用小流量探明实际限额,再定 TTL 与缓存命中率目标。
## FAQ
**Q1:免费接口也会被限流吗?**
会。文档写明「为防止滥用设有使用档次限制」,超出档位可能被限制调用。具体限额以官方免费 API 页面为准。
**Q2:缓存能完全避免限流吗?**
缓存能消除重复查询,大幅降低调用量,但是首次冷启动、缓存过期后的回源请求仍会计入档位。配合合理 TTL 与预热(热门诗人提前缓存)效果最佳。
**Q3:可以用 CDN/浏览器缓存吗?**
`appKey` 在 URL 中,浏览器侧缓存会把密钥与响应一起缓存、且有跨用户串数据风险,不建议。缓存应放在你自己的服务端(Redis/内存)。
**Q4:文档没给限流错误码,怎么判断被限流了?**
文档未公开具体限流码。若返回异常或 `remark` 提示受限,先暂停并以指数退避重试;同时检查是否因未缓存导致请求过量。
## 相关能力 / 下一步阅读
- [唐诗宋词元曲查询:page 与 maxResult=20 分页翻页拉取全部诗词](https://www.showapi.com/guides/poem-pagination-1620) — 翻页拉全量时务必配合缓存
- [唐诗宋词元曲查询返回结构全解:ret_code、dynastyInfo、poemInfo 一文读懂](https://www.showapi.com/guides/poem-response-fields-1620) — 字段与判断依据
- **本系列共 12 篇**:查看[唐诗宋词元曲查询指南总目录](https://www.showapi.com/guides/poem-guides-1620)