古籍查询 API 返回字段全解:trainslation 拼写坑与每个字段含义
# 古籍查询 API 返回字段全解:trainslation 拼写坑与每个字段含义
> 接口:古籍查询(apiCode 1643)· 接入点 1643-3「查询古籍明细」· **免费服务** · 请求方式 POST/GET · 返回 JSON
> 适用人群:要正确解析返回、做数据落库的开发者 · 阅读时间:约 6 分钟
## 核心要点
- 明细返回的核心是一个数组 `ancientBooksInfo[]`,每个元素是一部书的一个「篇章」。
- **译文的 JSON 键名是 `trainslation`(少一个 s)**,不是 `translation`——这是接口真实字段名,OpenAPI 文档也未列出,最易踩坑。
- `annotation`(注释)经常是空数组 `[]`,属正常数据缺失,不要据此判断失败;以 `ret_code == "0"` 为准。
## Why:为什么值得专门看这篇?
返回的字段名和字面意思不完全对应(译文不是 translation、没有"篇幅"字段、注释常空)。不做字段映射就直接 `data.translation` 会拿到 `undefined`。这篇把每个字段讲清,并给出解析模板。
## What:返回结构速览
业务数据在 `showapi_res_body` 内:
| 层级 | 字段 | 类型 | 含义 |
|------|------|------|------|
| body | `ret_code` | String | "0" 成功,其他失败 |
| body | `remark` | String | 提示信息,如"查询成功!" |
| body | `allNum` | Number | 该书的篇章总数(本页口径) |
| body | `allPages` | Number | 总页数 |
| body | `maxResult` | Number | 每页条数(默认 20) |
| body | `currentPage` | Number | 当前页 |
| body | `ancientBooksInfo` | Array | 篇章数组,详见下表 |
`ancientBooksInfo[]` 每个元素:
| 字段 | 类型 | 含义 | 注意事项 |
|------|------|------|----------|
| `title` | String | 书名 | 如"论语" |
| `titleId` | String | 该书 Id | 与 1643-2 返回的 titleId 一致 |
| `section` | String | **篇章名**(如"学而篇") | 注意:这是"篇/章"名,不是"篇幅/长度" |
| `author` | String | 作者 | 多为"佚名"等古籍署名 |
| `original` | Array[String] | 原文,按段落切分 | 与 `trainslation` 按索引一一对应 |
| `trainslation` | Array[String] | **译文**,按段落切分 | ⚠️ 键名拼写为 `trainslation`(少 s) |
| `annotation` | Array[String] | 注释 | ⚠️ 常为 `[]`,非错误 |
| `note` | String | 参考资料说明 | 如来源链接 |
| `ancientBookId` | String | 该篇章内部 Id | 区别于 `titleId` |
## How:正确解析(多语言)
Python:
```python
import requests
APPKEY = "YOUR_APPKEY"
TITLE_ID = "5b23316f618cd77360b91f0d"
body = requests.post(
f"https://route.showapi.com/1643-3?appKey={APPKEY}",
data={"titleId": TITLE_ID, "page": "1"},
headers={"content-type": "application/x-www-form-urlencoded"}, timeout=10
).json()["showapi_res_body"]
if body.get("ret_code") != "0":
raise RuntimeError(body.get("remark"))
for ch in body["ancientBooksInfo"]:
print("篇章:", ch["section"])
for i, para in enumerate(ch["original"]):
translation = ch["trainslation"][i] if i < len(ch["trainslation"]) else ""
print(f"原文[{i}]:", para)
print(f"译文[{i}]:", translation)
# 注释可能为空,先判断再处理
notes = ch.get("annotation") or []
if notes:
print("注释:", notes)
```
cURL(先看原始返回,确认键名):
```bash
curl -X POST "https://route.showapi.com/1643-3?appKey=YOUR_APPKEY" \
-H "content-type: application/x-www-form-urlencoded" \
-d "titleId=5b23316f618cd77360b91f0d&page=1" | python -m json.tool
```
Node.js(fetch):
```javascript
const APPKEY = "YOUR_APPKEY";
const TITLE_ID = "5b23316f618cd77360b91f0d";
const body = (await (await fetch(`https://route.showapi.com/1643-3?appKey=${APPKEY}`, {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ titleId: TITLE_ID, page: "1" })
})).json()).showapi_res_body;
if (body.ret_code !== "0") throw new Error(body.remark);
for (const ch of body.ancientBooksInfo) {
ch.original.forEach((para, i) => {
const translation = ch.trainslation[i] || ""; // 键名 trainslation
console.log(`原文[${i}]:`, para, "| 译文:", translation);
});
}
```
## 真实返回示例(节选)
```json
{
"showapi_res_body": {
"ret_code": "0",
"remark": "查询成功!",
"ancientBooksInfo": [
{
"title": "论语",
"titleId": "5b23316f618cd77360b91f0d",
"section": "学而篇",
"author": "佚名",
"original": ["子曰:“学而时习之,不亦说乎?……”"],
"trainslation": ["孔子说:“学了又时常温习和练习,不是很愉快吗?……”"],
"annotation": [],
"note": "参考资料: 1、 佚名.论语百科.http://lunyu.baike.com/article-29298.html",
"ancientBookId": "5b286b7e618c454cc40b5997"
}
],
"maxResult": 20,
"allNum": 20,
"allPages": 1,
"currentPage": 1
}
}
```
## 进阶 / 边界
- **对照阅读的最佳实践**:`original` 与 `trainslation` 都是数组且按段落对齐,用同一索引 `i` 配对展示最稳;但个别篇章两者长度可能不一致,配对前用 `min(len(original), len(trainslation))` 或逐个判空兜底。
- **没有"篇幅"字段**:产品描述里的"篇幅"在返回中并无独立字段,对应到 `section`(篇章名)。不要去找 `length`/`size` 之类字段。
- **`annotation` 为空是正常的**:很多古籍/篇章没有注释数据,返回 `[]`,与调用成功与否无关。
- **`note` 是参考资料**:通常是外部来源链接,展示时可做来源标注。
## FAQ
**Q1:为什么 `data.translation` 取不到值?**
A:真实键名是 `trainslation`(少一个 s)。改成 `data.trainslation`。
**Q2:OpenAPI 文档里没有 trainslation 字段?**
A:对,接口实际返回包含 `trainslation`,但官方 OpenAPI schema 未列它。以接口实测返回为准。
**Q3:original 和 trainslation 长度不一样怎么办?**
A:按索引配对时做长度兜底(取较小者,缺的一侧留空),避免越界。
## 相关能力 / 下一步阅读
- [古籍查询 API:按 titleId 获取古籍明细(原文 / 译文 / 注释)](https://www.showapi.com/guides/ancient-books-detail-1643)
- [古籍查询 API 分页机制:page / maxResult / allPages 怎么用](https://www.showapi.com/guides/ancient-books-pagination-1643)
- [古籍查询 API 常见问题与避坑指南(trainslation 拼写、注释为空、分页)](https://www.showapi.com/guides/ancient-books-faq-1643)
- **本系列共 10 篇**:查看[古籍查询 API 指南总目录](https://www.showapi.com/guides/ancient-books-guides-1643)