免费接口也别乱调用:童话故事集 API 内容缓存与分页拉取策略
# 免费接口也别乱调用:童话故事集 API 内容缓存与分页拉取策略
> 元信息:童话故事集 API(apiCode=1700)· 全接入点 · 免费服务 · 适用人群:已接入、准备上线的中高级开发者 · 阅读时间约 8 分钟
## 核心要点
- 分类(1700-1)与详情正文(1700-3)变化频率极低,适合做小时级甚至天级缓存。
- 列表(1700-2)按「分类+关键字」做短时效缓存,分页靠 `allPages` 顺序翻页。
- 免费服务有档次限制,缓存 + 限流能显著降低调用量、避免超限。
## Why:免费≠可以随便打
童话故事集 API 虽然是免费接口,但"免费"靠使用档次限制来兜底——高频、批量的循环调用很容易触顶。故事内容本身几乎不变(经典童话不会天天改),所以最划算的做法是把内容缓存起来,只在必要时回源。本篇给出可落地的缓存键设计与分页拉取写法。
## What:缓存价值速览
| 接入点 | 数据特征 | 建议缓存 |
|--------|----------|----------|
| 1700-1 分类 | 极稳定 | 缓存数小时~1 天 |
| 1700-2 列表 | 随分类+关键字变化 | 缓存数分钟~数十分钟(按 key) |
| 1700-3 详情 | 正文几乎不变 | 缓存数天~永久(按 id) |
## How:缓存键设计与分页拉取
### 1. 用 Redis 缓存详情(按 id)
```python
import requests, json, redis
rds = redis.Redis()
APPKEY = "YOUR_APPKEY"
def get_story(story_id):
cache_key = f"story:detail:{story_id}"
cached = rds.get(cache_key)
if cached:
return json.loads(cached) # 命中缓存,不回源
r = requests.post(f"https://route.showapi.com/1700-3?appKey={APPKEY}",
data={"id": story_id},
headers={"content-type": "application/x-www-form-urlencoded"}, timeout=10)
body = r.json()["showapi_res_body"]
if body.get("ret_code") != "0":
raise SystemExit(body.get("remark"))
rds.set(cache_key, json.dumps(body, ensure_ascii=False), ex=86400 * 7) # 缓存 7 天
return body
```
```javascript
const redis = require("redis"); const rds = redis.createClient();
const APPKEY = "YOUR_APPKEY";
async function getStory(storyId) {
const key = `story:detail:${storyId}`;
const hit = await rds.get(key);
if (hit) return JSON.parse(hit);
const r = await fetch(`https://route.showapi.com/1700-3?appKey=${APPKEY}`, {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ id: storyId }),
});
const body = (await r.json()).showapi_res_body;
if (body.ret_code !== "0") throw new Error(body.remark);
await rds.set(key, JSON.stringify(body), "EX", 86400 * 7);
return body;
}
```
### 2. 列表分页拉全(带限流)
```python
import time
def fetch_all(classify_id, keyword):
page, out = 1, []
while True:
r = requests.post(f"https://route.showapi.com/1700-2?appKey={APPKEY}",
data={"classifyId": classify_id, "keyword": keyword, "page": str(page)},
headers={"content-type": "application/x-www-form-urlencoded"}, timeout=10)
body = r.json()["showapi_res_body"]
out.extend(body["contentlist"])
if page >= int(body["allPages"]):
break
page += 1
time.sleep(0.2) # 控制频率,避免触档次限制
return out
```
## 返回示例与解析
分页关键字段(1700-2 返回 `showapi_res_body` 内):
```json
{ "allNum": "15", "allPages": "1", "currentPage": "1", "maxResult": "20" }
```
循环条件 `page >= int(allPages)` 即可拉完全部分页,无需猜页数。
## 进阶 / 边界
- **缓存失效策略**:详情正文若需更新(如纠错),用固定 TTL(如 7 天)自然过期,或主动 `DEL story:detail:{id}` 强制刷新。
- **列表缓存 key 要带全维度**:`story:list:{classifyId}:{keyword}:{page}`,否则不同分类/关键字会串数据。
- **限流兜底**:除缓存外,建议加令牌桶/信号量控制每秒请求数,超限时退避重试(指数退避)。
## FAQ
**Q: 缓存 7 天,内容更新了怎么办?**
A: 详情内容极稳定,7 天 TTL 后自然回源;若需即时更新,主动删除该 id 的缓存键即可。
**Q: 列表缓存多久合适?**
A: 列表比详情变化快,建议数分钟到数十分钟短缓存,且 key 必须包含 classifyId+keyword+page。
**Q: 免费接口也会限流吗?**
A: 会。免费服务靠使用档次限制兜底,具体阈值以官方说明为准;缓存+限流是最有效的规避手段(见[免费档位说明](https://www.showapi.com/guides/child-story-free-tier-1700))。
**Q: 能不能一次性拉全部故事建库?**
A: 没有批量接入点,只能逐级调用。建库时务必分页+限流+缓存,否则易超限。
## 相关能力 / 下一步阅读
- [童话故事集 API 免费档位说明](https://www.showapi.com/guides/child-story-free-tier-1700)
- [童话故事集 API:故事列表搜索与分页实战](https://www.showapi.com/guides/child-story-list-guide-1700)
- [儿童故事小程序全链路设计](https://www.showapi.com/guides/child-story-list-integration-1700)
- **本系列共 12 篇**:查看[童话故事集 API 指南总目录](https://www.showapi.com/guides/child-story-guides-1700)