最近在做一个古诗词小程序的时候,发现手动维护诗词库太费劲了。搜了一圈发现一个「随机诗词」的 POST 接口,支持按 10 种主题筛选,接口文档很清晰,于是决定直接接入试试。
这篇文章记录一下我从接口分析、参数梳理到 Node.js 完整对接的全过程,顺便把一些调试心得分享出来。

先看整体流程
打开接口文档页面 https://apizero.cn/marketplace/shici,能看到这是一个标准的 POST 接口:
- 请求方式:POST
- Content-Type:application/json
- 功能:随机返回一首古诗词
- 特色:支持按 10 种主题筛选
平台文档结构很清晰,先看请求格式,再看响应字段,最后写代码对接。
抓包:先摸清接口结构
我先用 Postman 打了一个最简单的请求,没有传任何筛选参数,想看看默认返回长什么样。
请求地址:POST https://v1.apizero.cn/api/shici
Headers:
| 字段 | 值 |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer {your_token} |
Body:
json
{}
返回结果:
json
{
"code": 200,
"message": "success",
"data": {
"title": "静夜思",
"author": "李白",
"dynasty": "唐",
"content": "床前明月光,疑是地上霜。举头望明月,低头思故乡。",
"theme": "抒情"
}
}
到这里基本清楚了------接口返回结构很规范,data 里包含诗词标题、作者、朝代、正文和主题分类。
关键参数:主题筛选
这里我重点看了一下主题参数。文档里列了 10 种主题,我整理成了一张表:

请求时只需要在 body 里加上 theme 字段:
json
{
"theme": "山水"
}
就能只返回山水类的诗词。这个设计挺实用的,不用自己在前端做过滤。
响应结构分析
我多打了几次请求,发现响应结构很稳定:

核心字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| code | number | 状态码,200 表示成功 |
| message | string | 响应消息 |
| data.title | string | 诗词标题 |
| data.author | string | 作者 |
| data.dynasty | string | 朝代 |
| data.content | string | 诗词正文 |
| data.theme | string | 主题分类 |
Node.js 完整对接
摸清了接口结构,接下来就是写代码了。我用的是 Node.js + axios,封装成一个可以直接调用的工具函数:
javascript
const axios = require('axios');
const API_BASE = 'https://v1.apizero.cn/api/shici';
const API_TOKEN = process.env.APIZERO_TOKEN; // 建议放环境变量
/**
* 获取随机诗词
* @param {string} [theme] - 可选主题:抒情/四季/山水/天气/人物/生活/节日/动物/植物/食物
* @returns {Promise<Object>} 诗词数据
*/
async function getRandomPoem(theme) {
const body = {};
if (theme) {
body.theme = theme;
}
try {
const response = await axios.post(API_BASE, body, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_TOKEN}`,
},
timeout: 5000,
});
if (response.data.code === 200) {
return response.data.data;
} else {
throw new Error(`API 返回异常: ${response.data.message}`);
}
} catch (error) {
console.error('获取诗词失败:', error.message);
throw error;
}
}
// 使用示例
(async () => {
// 获取任意主题的随机诗词
const poem1 = await getRandomPoem();
console.log(`[${poem1.theme}] ${poem1.title} - ${poem1.author}(${poem1.dynasty})`);
console.log(poem1.content);
console.log('---');
// 指定获取山水类诗词
const poem2 = await getRandomPoem('山水');
console.log(`[${poem2.theme}] ${poem2.title} - ${poem2.author}(${poem2.dynasty})`);
console.log(poem2.content);
})();
运行效果:
text
[抒情] 静夜思 - 李白(唐)
床前明月光,疑是地上霜。举头望明月,低头思故乡。
---
[山水] 山居秋暝 - 王维(唐)
空山新雨后,天气晚来秋。明月松间照,清泉石上流。
进阶:批量获取 + 去重
实际项目中我还需要一次获取多首不重复的诗词,这里写了个批量获取的辅助函数:
javascript
/**
* 批量获取不重复的诗词
* @param {number} count - 需要的数量
* @param {string} [theme] - 可选主题
* @returns {Promise<Array>} 诗词数组
*/
async function getMultiplePoems(count, theme) {
const poems = [];
const seen = new Set();
let retries = 0;
const maxRetries = count * 3; // 防止死循环
while (poems.length < count && retries < maxRetries) {
const poem = await getRandomPoem(theme);
const key = `${poem.title}-${poem.author}`;
if (!seen.has(key)) {
seen.add(key);
poems.push(poem);
}
retries++;
}
return poems;
}
调试过程中踩的坑
- Token 放在代码里:一开始图方便直接把 token 写死在代码里,后来想想还是改成了环境变量,安全第一。
- 超时设置:没加 timeout 的时候,网络抖动会导致请求卡住,加了 5 秒超时后稳定多了。
- 主题字段大小写 :试过传
SHANSHUI,发现不认,必须用中文。
前端展示小技巧
拿到诗词数据后,前端展示可以用卡片式布局,标题大字、正文居中、作者信息放右下角。如果做小程序的话,还可以配合「分享到朋友圈」功能,让诗词自然传播。
html
<div class="poem-card">
<h2 class="poem-title">静夜思</h2>
<p class="poem-dynasty">唐 · 李白</p>
<div class="poem-content">
<p>床前明月光,疑是地上霜。</p>
<p>举头望明月,低头思故乡。</p>
</div>
<span class="poem-theme">抒情</span>
</div>