简介:一份中国诗词大全 JSON 版完整压缩包,源自 GitHub 的 chinese-poetry 开源项目,专供需要本地化处理中文诗词数据的开发者、数据分析师与 NLP 研究者使用,解决 GitHub 直接下载缓慢、clone 容易中断的痛点。压缩包共 1371 个文件,以 1339 个 JSON 数据文件为主体,覆盖唐诗、宋词、元曲、五代词及作者小传等结构化内容,部分文件记录了数万首诗人作品与索引;另含 Markdown 说明文档、SQLite 数据库、辅助 JavaScript 脚本及少量图片,整包约 84.85MB,便于离线检索与批量解析。目前已有 1065 人学习下载,适合需要快速获取高质量诗词语料的中高级 Python 使用者。资源整理自社区开源项目,目录层级清晰、文件命名规范,可直接导入 Elasticsearch、MongoDB 等环境,或用于构建诗词检索问答、文本挖掘与语料训练任务,能显著节省网络访问与数据清洗时间。
1. 为什么我要把《全唐诗》《全宋词》整理成一份JSON
1.1 从一次古诗词App开发经历说起
今年年初我给一个文化类App做内容模块,需求很简单:做一个"每日诗词"卡片,每天推荐一首古诗词,附带作者、朝代、正文。需求听起来不难,但真正动手时才发现——拿不到一份干净、结构化、能直接入库的古诗词数据。
网上搜一圈,能找到的基本是这几种:
- 数据库备份文件(.sql),几十MB起步,结构和字段千奇百怪;
- Excel/CSV表格,格式混乱,有的把整首诗的标题、作者、正文挤在一个单元格里;
- 网页爬虫教程,让你自己去抓公开诗词网站,但那些页面本身就存在乱码、重复、缺字问题;
- 还有一部分是各种"全唐诗大全"的txt文本,一行一首,断句和标点全靠猜。
折腾一周之后我放弃了,决定按自己的标准整理一份"诗词大全JSON版"。
这份数据的目标很明确:拿到手就能用。无论是做App后端、写前端页面、搞NLP训练,还是做个简单搜索工具,都不需要再写一堆恶心的正则去清洗数据。文章末尾我会把这份JSON压缩包的关键结构和踩坑经验完整拆开讲,希望能帮正在做类似内容的同学少走弯路。
1.2 市面上的开源诗词库为什么都不顺手
我不是说网上没有好东西,像一些古籍数字化项目确实做得很好,但它们的定位偏向"学术研究",不太适合直接对接业务开发:
- 字段太杂,注释、异文、校勘记全堆在一起,一个字段能写一整段话;
- 按"古籍原书"组织,而不是按"一首诗"组织,想要随机取一首诗还得自己切分;
- 编码和格式不统一,同一部诗集里简体繁体混用、全角半角混用。
我要做的是面向工程实践的版本:以"单首诗词"为最小单元,统一字段、统一编码、统一格式,再压缩成zip发布。
2. 数据结构设计:一个字段一个坑,我这样建模
2.1 顶层结构:分文件还是合成一个大JSON
这是最早纠结的问题。全唐诗四万多首,全宋词两万多首,加上宋诗、元曲、诗经、楚辞、乐府等,总量在十万首以上。
方案有两种:
- 全部塞进一个超大JSON文件(比如
poetry_all.json); - 按诗集或朝代拆分成多个JSON文件,再用一个索引文件manage。
我最终选了第二种。原因有三点:
- 单文件超过100MB,很多编辑器和工具打开就直接卡死;
- 业务端往往只需要某一类数据,比如只做唐诗功能,没必要加载全部;
- 分文件之后,Git diff、增量更新、按需下载都方便。
压缩包里的顶层目录大致是这样:
poetry-json/ ├── README.md ├── index.json ├── 唐诗/ │ ├── 初唐.json │ ├── 盛唐.json │ ├── 中唐.json │ └── 晚唐.json ├── 宋词.json ├── 宋诗.json ├── 元曲.json ├── 诗经.json ├── 楚辞.json └── 乐府.jsonindex.json是一份总目录,记录了每个文件对应的诗集名、朝代、收录数量、文件大小,方便程序先加载索引,再按需加载具体文件。
2.2 单首诗词的字段设计与我的取舍
每首诗词我定义为如下结构:
{ "id": "ts-0001", "title": "静夜思", "author": "李白", "dynasty": "唐", "type": "诗", "collection": "唐诗·盛唐", "paragraphs": [ "床前明月光", "疑是地上霜", "举头望明月", "低头思故乡" ], "tags": ["五言绝句", "思乡", "写景"], "notes": "" }字段设计上有几个有意的取舍,值得说明一下:
正文为什么要存成数组而不是整段字符串?
因为绝大多数使用场景都需要"按行"处理——前端展示要逐行排版,做飞花令要按句子匹配,做鉴赏需要定位到具体某句。如果存成一个string,每次用还得split,而且不同来源的换行符还不一样(\n、\r\n都有),存成数组直接从源头规避了这个问题。
为什么要加type字段(诗/词/曲/赋)?
因为"唐诗""宋词"这种按朝代分类其实很粗糙——宋朝人也写诗,唐朝也有词。type字段用来表达文体,collection表达所属合集,两个维度分开,业务端筛选时很灵活。
id命名的规律
id前缀对应来源:ts唐诗、sc宋词、ss宋诗、yq元曲、sj诗经、cf楚辞、yf乐府,后面是四位序号。这样看到id就知道数据归属,而且在合并数据时不会撞id。
2.3 为什么我坚持保留"俗体字"和"异体字"
清洗时最纠结的问题是:要不要做繁体转简体?
我的决定是——保留原汁原味的字形录入,但额外提供简体版本字段,也就是说在paragraphs之外再加了一个可选的paragraphs_simple数组。如果原文是繁体,paragraphs用繁体,paragraphs_simple用简体。如果原文本来就是简体,两个字段保持一致。
原因很简单:做文学研究的人需要看原文,做产品的人需要给普通用户看简体,两个需求都不能得罪。而且古诗词里很多字"简体化"之后反而丢失了平仄和韵脚的信息,比如"雲"和"云"在某些语境下不能混用。这个设计牺牲了一些存储空间,但换来了极大的兼容性。
3. 数据清洗实录:上万处标点和重复条目是这样修掉的
3.1 初步去重:同一首诗以不同标题反复出现
整理过程中最大的噩梦是重复。同一首李白诗,在《全唐诗》里收录的是正题,但在《唐诗纪事》里可能换了标题或删了几句。如果只按"标题+作者"去重,基本没用。
我的策略是:按正文的"指纹"去重。具体做法:
import hashlib import json def poem_fingerprint(paragraphs): # 去掉所有非汉字字符,拼接后做 hash text = "".join(paragraphs) text = "".join(ch for ch in text if "\u4e00" <= ch <= "\u9fff") return hashlib.md5(text.encode("utf-8")).hexdigest()两个关键点:
- 用MD5而不是直接比对全文,是因为十万首诗做两两对比,字符串比较太慢,哈希可以提前索引;
- 只保留汉字,去标点、去空格、去换行,是为了避免同一首诗因为标点处理方式不同被误判成两首。
指纹相同后,再人工确认保留哪一条:优先保留带注释的,标题更规范的,来自更权威合集的。
3.2 标点与断句规范化:全角、半角、弯引号
原始文本来源复杂,有从PDF转出来的、有从网页复制下来的、有扫描OCR的。标点问题千奇百怪:
- 英文标点混入,比如用
,代替,; - 弯引号(
“”)和直引号("")混用; - 句末有的用
。,有的用.,有的干脆没有; - 常见的是断句错误:五言诗断成了"床前/明月光"、"疑是/地上霜"这样带斜杠的格式,或者两句并成一句。
我写了一个清洗管道,按顺序处理:
- 把所有全角英文字母、数字转半角;
- 把半角标点统一转全角(中文语境下句号、逗号、顿号、引号都该是全角的);
- 按"韵脚+句长"规则做断句校验:五言诗每句5个字、七言诗每句7个字,如果句子长度不对,就标记出来人工复核。
断句校验这个步骤特别有用。因为古诗词有严格的字数规律,一首七言绝句如果某句是8个字,那基本可以确定清洗有问题。
3.3 作者信息纠错与朝代补全
作者字段的坑比想象中多:
- 同一作者多种署名:"李白"有时写作"李太白"、"青莲居士";
- 生卒年跨朝代的人,归属争议大,比如李煜算"五代"还是"宋";
- 部分佚名作品的作者字段是空的。
我的做法是维护一份作者规范映射表:
{ "raw": "李太白", "canonical": "李白", "dynasty": "唐", "alias": ["青莲居士", "谪仙人"] }先建立别名映射,再在清洗时做全量替换。朝代字段不依赖原始文本,而是以作者规范表为准,这样一来,"李煜"的作品默认为"五代",想看宋词的人即使不设置filter也基本不会混入。当然,这个方案不是完美的——我承认它在文学考证上有妥协,但对工程使用来说,它换来了可预期的确定性。
3.4 自动化校验:让十万首诗词的清洗结果可验证
清洗完不能直接打包发布,必须有一套校验规则:
- 数量校验:每个合集的实际条数和预期条数对比,误差大于1%就报警;
- 字段完整性:必填字段(id、title、author、paragraphs)为空的数据占比要低于0.1%;
- 长度校验:五言诗的每句长度==5,七言诗每句长度==7,词牌不限制但上下句长度需要和词牌规则里的常见范围匹配;
- JSON Schema校验:每个文件都用预定义的Schema检查字段类型是否正确。
这套校验脚本跑完之后,我打包前还会做一次抽样人工阅读:每个朝代随机抽20首,逐首读一遍,确认句子通顺、意境完整。机器校验防的是系统性错误,人工抽读防的是机器判断不了的"语义断裂"。
4. 压缩包目录详解:拿到手先看这几份文件
4.1 我的目录取舍:为什么不放SQL和CSV
确实有用户提过——"能不能同时给一份SQL?"我的答案是:JSON是源格式,其他格式你需要时自己转。原因很直接:
- JSON是树形结构,可以直接表达"一首诗下面的多个句子、多个标签",而SQL要拆成多张表再join,怎么存都别扭;
- CSV对于含换行、逗号、引号的文本字段是灾难——诗词正文里既有逗号又有引号,不做转义处理基本是坏的;
- 目前Python、JavaScript、Java等主流语言处理JSON都是原生支持,解析开销可控。
压缩包里除了前面提到的JSON文件,还有一份README.md,里面写了:
- 字段说明和取值枚举;
- 更新日志和版本号;
- 引用来源声明;
- 常见使用示例。
4.2 三种常见语言的加载示例
Python(用于数据处理和脚本化使用):
import json with open("宋词.json", "r", encoding="utf-8") as f: data = json.load(f) # 筛选李清照的词 for poem in data["poems"]: if poem["author"] == "李清照": print(f"{poem['title']}: {poem['paragraphs'][0]}")Node.js(用于前端或服务端):
const fs = require('fs'); const data = JSON.parse(fs.readFileSync('宋词.json', 'utf-8')); const liQingzhao = data.poems.filter(p => p.author === '李清照'); console.log(liQingzhao);Java(用于Android或后端):
// 用 Jackson 解析 ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readTree(new File("唐诗.json")); for (JsonNode node : root.get("poems")) { if ("李白".equals(node.get("author").asText())) { System.out.println(node.get("title").asText()); } }5. 使用JSON数据时最容易踩的坑,以及我如何规避
5.1 编码问题:UTF-8带不带BOM差别很大
这是最隐蔽的一个坑。在Windows上,默认的记事本保存UTF-8文件时会自动加上BOM头(EF BB BF),这会导致JSON解析器直接报错或者解析出不可见字符。
- Python的
json.load遇到带BOM的文件会直接抛json.decoder.JSONDecodeError; - Java的Jackson在部分版本下会遇到
Invalid UTF-8的问题。
打包前我用脚本扫描了所有文件,把BOM全部去掉,统一为UTF-8无BOM。具体命令:
# Linux / macOS find . -name "*.json" -exec sed -i '1s/^\xEF\xBB\xBF//' {} \;5.2 Java后端场景:大写字母开头的字段变小写
之前有读者问过我,解析后Title变成了title,查了半天不知道哪里出了问题。这是Jackson等JSON库的命名策略导致:如果Java Bean的字段是String Title,默认的camelCase策略会把它序列化成title,而如果JSON里本来就写了Title,反序列化时又匹配不上。
我在README里特别提醒了这一点:如果你们的后端接口要用@JsonProperty注解显式指定字段名,不要依赖默认命名策略:
public class Poem { @JsonProperty("id") public String id; @JsonProperty("title") public String title; @JsonProperty("author") public String author; @JsonProperty("paragraphs") public List<String> paragraphs; }这样写最保险,不管把JSON文件喂给哪个JVM语言,字段映射都不会跑偏。
5.3 前端场景:"[object Object] is not valid JSON"的真相
很多网页在加载JSON时会在控制台报一个非常误导人的错:
Uncaught SyntaxError: "[object Object]" is not valid JSON这个错误99%的情况是:你试图对一个JavaScript对象调用JSON.parse,而不是对字符串调用。比如:
const data = { title: "静夜思", author: "李白" }; // 错误写法:JSON.parse 接收了一个对象 const result = JSON.parse(data); // 正确写法 const result = JSON.parse(JSON.stringify(data)); // 或者如果是从外部 fetch 得来,response.json() 本身已经是解析后的对象 const result = await response.json();如果发现报错中出现了[object Object],第一反应应该是去检查:传给JSON.parse的参数是不是一个对象,而不是一个字符串。
5.4 浏览器直接打开JSON文件的白屏问题
用file://协议直接在Chrome或Edge里打开本地JSON并尝试用fetch加载,会被浏览器的同源策略拦下来,窗口一片空白。这和你的代码无关,是安全策略。
实际上,我在README里就对前端开发者做了如下建议:在本地开发时起一个简单的静态服务,而不是直接双击HTML文件。最简单的方式:
# 在项目根目录执行 python3 -m http.server 8080 # 然后浏览器访问 http://localhost:8080/index.html这样fetch('唐诗.json')才能正常工作。
5.5 大JSON文件解析的性能问题
说句实话,即使是单朝的JSON,也有几十MB。老式做法是一次性JSON.parse或json.load全量读入,在小设备上确实可能卡顿或内存溢出。
我做了两件事来缓解:
- 分文件存储,按需加载;
- 在README里推荐流式解析方案。
Python端可以用ijson做流式解析;前端可以用fetch加流式读取(ReadableStream)或者做懒加载,在滚动到对应位置时才加载具体文件。
这个数据集毕竟不是为大数据场景设计的,如果你要做的是千万条的全文分析,建议先导入ClickHouse或MongoDB,再建立索引。不要让JSON文件本身承担数据库的职责。
6. 从JSON到应用:几个落地场景与我的扩展建议
6.1 全文搜索与条件筛选
拿到JSON后,最常见的需求就是"按作者查诗""按朝代筛选""按标签找主题"。最简单暴力的方式是加载后遍历过滤,数据量在十万级别时性能其实还能接受,但如果要做全文搜索(搜诗句中的某个词),建议导入Elasticsearch或SQLite的FTS5。
举个例子,用SQLite做诗句搜索:
CREATE VIRTUAL TABLE poems_fts USING fts5(title, author, paragraphs);把JSON数据灌进去之后,搜"明月"只需要一条SQL,速度快很多。
6.2 随机诵读与每日推送
paragraphs设计成数组的好处在这个场景体现得很充分。做每日诗词卡片时,只需要:
- 加载对应朝代的JSON;
- 随机取一个index;
- 从
paragraphs里按行渲染。
不需要再做任何字符串处理,直接交给前端展示。我自己的小工具还加了一个"飞花令"模式——给一个关键字,遍历所有诗句匹配包含该字的句子,paragraphs数组逐条匹配,逻辑非常简洁。
6.3 NLP分析与古诗生成
如果要做古诗风格分析,建议使用paragraphs_simple(简体版本)做向量化,保留的繁体字段做对照。注意一点:不要对平仄做自动推断,除非你清楚古音和今音的差异,否则拿普通话四声去套平仄,结果会很离谱。
6.4 我实际做完之后的几个体会
整理这套诗词JSON的过程,本质上是在做一次"内容工程"。它不像写业务代码那样有即时反馈,而是一个需要耐心校验和反复修正的过程。但做完之后,后续开发效率提升是肉眼可见的:
- 新功能不再被数据清洗卡住,数据质量的可预期性非常高;
- 出问题能快速定位到具体某一首诗的某一句,而不是在一堆txt里大海捞针;
- 和其他项目对接时,直接甩一个README加JSON文件过去,别人照着示例就能跑通。
最后分享一个小技巧:发布数据包时,一定要在index.json里明确写一个"version": "1.0.0"字段。后来你会发现,这是整个包里性价比最高的一个字段——别人引用数据的时候能说清楚用的哪个版本,你更新数据的时候也不会引起下游一片乱。就这个字段,能省掉你至少十次解释"你用的为什么和我用的不一样"的沟通成本。
本文还有配套的精品资源,点击获取