简介:这份中国诗词大全JSON版完整压缩包,原始数据取自代码托管平台 GitHub 上的开源项目 chinese-poetry,专为解决国内访问与下载速度缓慢的问题而重新打包,面向需要处理中文诗词数据的研究者、开发者和数据爱好者。压缩包内共包含一千三百七十一个文件,整体大小约八十四点八五兆字节,核心内容以 JSON 格式存储,收录唐诗、宋词、元曲等大量诗词原文、作者信息与篇目数据,同时附带若干 Markdown 说明文档、Python 处理脚本、SQLite 数据库以及配置文件,便于直接解析、导入数据库或进行二次开发。目前已有 1083 人学习或下载,适用于自然语言处理、中文语料分析、诗歌风格研究、文本挖掘等场景。拿到手即可从作者、词牌、朝代、篇章等多个维度开展统计与挖掘,省去自行爬取、清洗和整理的繁琐步骤,是一份可直接上手、方便离线使用的开源中文诗词数据集合。
1. 为什么你需要一份「中国诗词大全 JSON 版」而不是再写一次爬虫
做诗词类应用、学习工具、数据可视化大屏,或者只是想给大模型喂一批中文语料,最卡人的往往不是功能代码,而是数据本身。市面上的诗词文本散落在各类网站里,带格式、带广告、带残缺的标点,抓下来还要花大力气清洗;真正结构化、字段统一、直接能读进程序的诗词数据集并不好找。「中国诗词大全 JSON 版」这类完整压缩包,就是把整理好的诗词文本按统一字段放进 JSON 文件,再打成压缩包分发,省掉你从零抓取、清洗、建模的整个前置阶段。适合谁?适合做前端页面想内嵌离线诗词库的开发者、做文本分析和可视化的学生,以及需要批量中文语料做实验的研究者。这篇就按我实际用这类数据集的路径,把结构设计、包内取舍、加载方式、常见坑一次讲透。
2. 诗词 JSON 的数据结构:字段设计决定你能做什么
2.1 一份能用的诗词 JSON,至少要有这 7 个字段
拿到压缩包先别急着写业务代码,第一步是打开一个 JSON 文件,看清字段结构。我见过太多人上来就遍历data,写了一半发现作者字段叫poet,朝代字段叫dynasty,赏析直接带 HTML 标签,整个加载逻辑推倒重来。先花十分钟看结构,后面省一小时。
一份组织良好的诗词 JSON,单条数据一般长这样:
{ "id": "tang-00001", "title": "静夜思", "author": "李白", "dynasty": "唐", "paragraphs": ["床前明月光", "疑是地上霜", "举头望明月", "低头思故乡"], "notes": "此诗写远客思乡之情……", "translation": "明亮的月光洒在窗户纸上……", "appreciation": "以清新朴素的笔触,抒写了深远隽永的乡情……" }这里每一个字段都不是随便定的。id是全库唯一主键,用于去重、做书签、做收藏,没有 id 的库后续做关联会非常痛苦,我建议你优先确认这个字段是否存在。title和author是检索入口,dynasty用于按朝代筛选,这三者构成最基础的元数据。paragraphs我用的是数组而不是整段字符串——诗词讲究排版和分联,数组能保留句间结构,前端渲染时逐句控制样式、做逐句注释、按句对齐译文都更方便;用一个长字符串也能跑,但遇到词牌名上下阕分割、遇到唱和诗,处理起来就要多写正则。
notes是原注或作者自注,translation是白话译文,appreciation是赏析。这三个字段不是每条数据都有,文本型数据集里注释和赏析的覆盖率通常低于正文,这不影响使用,但做统计时要注意空值。批量导入数据库前,建议先跑一遍字段完整性检查,统计每个字段的空值率,做到心里有数。
另外注意一种常见变体:有些打包版本把paragraphs写成content,把dynasty写成era,语义一样,但代码里的字段名要跟着改。我一般会在加载层做一次字段映射,统一成自己项目的命名,避免业务代码里到处判断别名。一个简单做法是写个转换函数,把数据集的字段名映射成内部标准字段,后续所有模块只认标准字段。
2.2 朝代、作者归并与异体字处理:数据清洗的三个坚持
压缩包里的数据不是天上掉下来的,整理者从古籍和网络文本聚合时,会留下一些「历史遗留问题」。用的时候要有心理准备,常见的是朝代名不一致:同一个诗人,有的记录是「唐」,有的记录是「唐朝」,有的干脆填「唐代」。作者名也有类似情况,字、号、别名混用,比如同一人可能被记录为「苏轼」和「苏东坡」。这属于数据归并问题,在使用前最好过一次归一化处理。
我的处理原则有三个。第一,朝代统一成单字或双字的标准称谓表,做一层映射字典,把「唐朝」「唐代」「大唐」全部归到「唐」,这样可以保证筛选统计时聚合正确。第二,作者名以通用正名为准,把常见别名并入正名,但要在数据里保留一个alias字段存原始写法,别直接覆盖,否则以后想追溯原始文本就找不回来了。第三,异体字和通假字不要强行统一,比如「閒」和「闲」、「裏」和「里」,在古籍电子化里经常混用,强行简体化会把诗句改得面目全非。做数据展示时保持原文,做检索时再做映射即可。
这三个坚持看起来简单,实际做的时候会发现牵一发动全身。比如朝代归并,元曲里有些作者被归在「元」和「金」两个朝代,同一个人的作品被拆到两个文件里,按朝代统计时数据就裂了。我处理某图像处理Demo的数据源时碰到过类似问题,最后是靠「作者+作品风格」双重判断才合并回来。所以拿到压缩包,第一步别急着展示,先跑一遍分组统计,看看朝代分布、作者分布有没有明显异常,再决定要不要做归并。
2.3 完整版与精简版的取舍:什么情况选哪个
「完整压缩包」这个词,实际里面经常包含两个版本:一个全量 JSON,一个精简 JSON。全量版带注释、译文、赏析,体积大;精简版通常只保留标题、作者、朝代、正文,体积可能只有全量版的几分之一。取舍的根据是你的使用场景,而不是追求「越全越好」。
如果你做的是离线词典类应用,用户查一首诗要看注释和赏析,那必须用全量版,但要做按需加载——先加载索引,用户点进去再看详情,避免冷启动时一次性解析几十上百 MB 数据。如果你做的是语料统计、词频分析、模型训练,精简版就够了,多余字段只会拖慢预处理速度。如果你做的是前端页面滚动加载全部诗作,比如做一个「诗词瀑布流」的展示效果,那体量才是关键,精简版是明显更合理的选择。
另外提醒一句:全量版里如果译文和赏析是后来人工补充的,不同批次的补充质量会参差不齐,有的赏析只有一句话,有的赏析整段复制了百科内容,字段长度差异巨大。做展示时要对超长文本做截断或折叠处理,别让一两条异常数据毁了整个页面的排版。我一般会先统计一下appreciation字段的长度分布,把明显超长的条目单独拿出来看一眼再入库。
3. 压缩包里的文件组织:单文件、分目录还是带索引
3.1 三种打包方式的适用场景
拿到压缩包解压后,不同的整理者有不同的文件组织习惯,常见的有三种。第一种是单文件全量打包,一个all_poems.json塞下所有数据,结构最简单,但解析时内存压力大,浏览器里加载尤其要小心。第二种是按朝代分目录,tang/、song/、yuan/各自放一个 JSON,支持按朝代懒加载,体积可控。第三种是全量数据加独立索引文件,index.json里只存 id、标题、作者、朝代这几个轻量字段,正文和赏析放在另一个文件里,查列表时只读索引,点详情再读全量。
三种方式对应三种典型场景。单文件适合桌面端脚本处理,Python 或 Node 跑批处理任务,一次性读完内存不是问题。分目录适合移动端或者网页端,用户按朝代浏览,只加载当前朝代的数据,体验顺滑。带索引的结构适合做搜索功能,索引文件小,加载快,搜索建议和结果列表都能直接从索引里出,等用户打开详情页再异步取全量文本。
我自己更偏向第三种结构。原因很实在:词频统计、作者排名、随机出题这些高频操作只需要元数据和正文开头几个字,根本用不着译文赏析;而用户真正打开一首诗看全文的场景,才需要拉全字段。索引与全量分离,可以让 80% 的轻量操作不碰大文件,这个收益在数据量大的时候非常明显。
3.2 打开压缩包先看这 4 个文件
不管压缩包内部怎么组织,有几个文件几乎一定会出现,或者说你应该主动去找它们。拿到压缩包第一件事不是解压全部内容,而是先扫一眼文件清单,确认有没有下面这些条目。
| 文件/目录 | 作用 | 使用建议 |
|---|---|---|
README.md或说明.txt | 数据来源、整理时间、字段说明 | 第一个打开,先读 |
all_poems.json或分朝代目录 | 正文数据 | 抽查 3-5 条看字段质量 |
LICENSE或版权声明 | 数据可用的法律边界 | 商用前务必确认 |
校验文件(如checksum.txt或.md5) | 压缩包是否完整、文件是否被篡改 | 解压后先校验 |
先读 README 这条,看起来像废话,实际上特别管用。许多打包者在 README 里会写明数据的整理时间和覆盖范围,有些还会标注已知问题,比如「部分宋词缺少注释」或「元曲作者朝代归属有争议」,这些信息直接决定你后续要不要花时间补数据。我拿到一个不熟悉的数据集,一定是先读说明文件再动代码,能避免很多返工。
LICENSE这条要单独强调。诗词原文本身是公共领域的文化遗产,但注释、译文、赏析这些衍生内容是有版权的。如果你做的产品有商业用途,一定要确认这些衍生文本的授权范围;没有 LICENSE 文件的包,商用前最好换一个来源明确的数据集,别把自己置于被动境地。数据合规这事,出问题就是大事,值得多花十分钟查清楚。
3.3 校验数据完整性的两条命令
解压之后、写代码之前,花三十秒做一次完整性校验,能避免「跑了一半发现文件截断」这种让人抓狂的情况。如果压缩包里带了校验文件,直接用校验工具核对:
# 在解压目录下执行,校验当前目录所有文件的哈希值 md5sum -c checksum.txtmd5sum -c会逐行读取校验文件里的「哈希值+文件名」,与当前目录文件实际算出的哈希做对比,全部通过会输出OK,不匹配会提示文件名并标明FAILED。注意checksum.txt里的文件路径是相对路径,要在解压后的根目录执行才能正确匹配;有人直接在下载目录执行,校验全报失败,其实是路径不对。
如果包里没带校验文件,就检查 JSON 本身的合法性:
# 检查单个 JSON 文件的格式是否完整 python3 -m json.tool all_poems.json > /dev/null && echo "JSON OK"python3 -m json.tool会把 JSON 重新格式化输出,如果文件中间截断或有多余逗号,它会报Expecting ',' delimiter或Expecting value之类的错,并给出出错行号;> /dev/null是把格式化后的内容丢弃,只看校验结果。大文件解析需要几秒,属于正常现象,别以为卡死了。
文件能从 JSON 解析出来只是第一步,数据内容本身可能还有问题。解析成功后拿一个已知条目核对,比如找一首确定的诗,看看标题作者朝代是否匹配、段落数是否符合记忆,这些抽查能发现整理逻辑上的结构性错误。有的人解压后直接入库,跑查询才发现同一首诗正文和标题对不上,回头再清洗就是好几天的活。
4. 在本地把诗词 JSON 用起来:加载、检索与统计的最小实践
4.1 用 Node.js 三分钟跑一个诗词检索接口
数据在手,第一个想做的往往是「能查诗」。不引入数据库,直接用 Node.js 起一个本地服务,按标题、作者、朝代做检索,是最快的验证方式。这个方案适合本地调试、小型内网工具、或者课程设计里的 demo 系统。
// poem-server.js const http = require('http'); const fs = require('fs'); // 读取精简版数据,避免加载译文赏析拖慢启动 const poems = JSON.parse(fs.readFileSync('./poems.min.json', 'utf-8')); const server = http.createServer((req, res) => { const url = new URL(req.url, 'http://localhost'); const keyword = url.searchParams.get('q') || ''; const dynasty = url.searchParams.get('dynasty') || ''; // 先按朝代过滤,再按关键词匹配标题和作者 const result = poems.filter(p => { if (dynasty && p.dynasty !== dynasty) return false; if (!keyword) return true; return p.title.includes(keyword) || p.author.includes(keyword); }).slice(0, 20); // 限制返回条数,避免响应体过大 res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.end(JSON.stringify({ total: result.length, data: result })); }); server.listen(3000, () => console.log('诗词检索服务运行在 http://localhost:3000'));代码逻辑很简单:用fs.readFileSync一次性把 JSON 载入内存,内存里维护一个数组,每次请求用filter做线性过滤。这里有两个参数要说明。q是搜索关键词,匹配标题或作者;dynasty是朝代筛选,两个参数可以组合使用。slice(0, 20)是结果数量上限,几千首诗词时可以不设,数据量到几万首时必须限制,否则一个模糊匹配可能返回几千条结果,响应体直接卡崩浏览器。
启动方式是在项目目录执行node poem-server.js,然后访问http://localhost:3000/?q=明月&dynasty=唐试一下。这个写法有几个明显的边界:内存占用会线性增长,数据量大到几十万条时不合适;includes是全词模糊匹配,搜「月」会把「月」字出现在标题和作者里的全捞出来,不是字段化检索。但对一个快速验证用的本地接口来说,这已经够用。
4.2 高频词与朝代分布的统计脚本
快速验证数据质量的最好方法,是跑一遍统计。统计结果能立刻暴露数据里的结构性畸形,比如朝代分布极不均衡、作者名字出现乱码、字段大量缺失。用 Python 写个统计脚本是最顺手的:
# stats.py import json from collections import Counter with open('poems.min.json', 'r', encoding='utf-8') as f: poems = json.load(f) # 统计作者作品数 Top10 author_counter = Counter(p['author'] for p in poems if p.get('author')) print('作者作品数 Top10:') for author, count in author_counter.most_common(10): print(f' {author}: {count}') # 统计朝代分布 dynasty_counter = Counter(p.get('dynasty', '未知') for p in poems) print('\n朝代分布:') for dynasty, count in dynasty_counter.most_common(): print(f' {dynasty}: {count}')Counter是 Python 内置的计数工具,most_common(10)直接返回频次最高的前十个。统计作者可以帮你验证数据集的覆盖范围,如果排第一的不是大家预期里的那几个名字,或者数据总量明显偏少,说明数据集可能只是节选而不是全量。统计朝代分布则直接反映数据的结构比例,正常来说唐诗宋词占比最大,元曲明清诗相对少,如果出现某个朝代数据量为零,多半是数据在整理时有分类遗漏。
更细致的字段质量检查,可以统计正文为空的条目数量:
# 检查正文缺失的条目 empty_body = [p for p in poems if not p.get('paragraphs')] print(f'正文缺失条目数: {len(empty_body)}')正文缺失的条目如果是零星几个,可能是原数据就没收录;如果上百上千,就要怀疑是不是字段名搞错了。比如某个打包版本把正文放在content字段而不是paragraphs,你用paragraphs取值自然全是空。遇到这种情况,先打印一条原始数据看实际字段名,再回头改脚本。
4.3 浏览器端直接加载的注意点
不少人是想把诗词数据放进纯前端页面,不打算起后端服务。这种用法可行,但有几个注意点绕不开。首先,JSON 文件不能直接用fs读取,浏览器里读取本地 JSON 只有两种常规方案:用fetch请求同源文件,或者把 JSON 转成 JS 对象直接塞进<script>标签里。fetch在本地通过file://协议打开 HTML 时会因跨域问题直接失败,所以本地调试时要么起一个静态服务器,要么把 JSON 包装成 JS 文件。
最常见做法是准备一个poems-data.js,内容写成变量赋值:
// poems-data.js const POEMS_DATA = [{"id":"tang-00001","title":"静夜思","author":"李白"}];然后在 HTML 里正常引入:
<script src="poems-data.js"></script> <script> // 此时可以直接使用 POEMS_DATA console.log(POEMS_DATA.length); </script>这种方式的好处是绕开了所有跨域和异步加载问题,页面初始化时变量就可用;坏处是如果数据量到几万条,这个 JS 文件的解析会阻塞首屏渲染,用户要白等好几秒。所以浏览器端加载的精简版数据要控制体量,几千首诗词的元数据做前端展示完全够用;超过这个量级,建议还是切到按朝代分文件、滚动到对应区域再动态加载<script>的方案。还有一种声音是把数据塞进 IndexedDB 做离线缓存,但对大多数诗词展示场景来说属于过度设计,启动慢、代码复杂、收益有限,我一般不优先推这个方案。
5. 诗词 JSON 避坑清单:编码、缺字与文本质量的 5 条踩坑记录
5.1 打开文件全是乱码:UTF-8 与 BOM 的坑
现象:用 Python 的open()读取 JSON 后,打印第一条数据,标题和作者全是锟斤拷之类的乱码,但用记事本打开同一个文件却显示正常。
原因:文件实际是带 BOM 的 UTF-8 编码,utf-8读取时 BOM 头没有被正确处理;或者文件本身是 GBK 编码,用 UTF-8 硬解自然全乱。很多编辑器保存文件时默认带 BOM,而 Python 和 Node 的默认 UTF-8 解析不会自动剥离 BOM。
解决:读取时显式指定编码,utf-8-sig会自动去除 BOM;如果确认文件是 GBK 编码,则用gbk或gb18030读取,后者兼容性更好,能解部分单字缺失。数据入库前统一转成无 BOM 的 UTF-8,可以避免后续所有环节再踩这个坑。检查文件的编码,可以用 VS Code 右下角的编码显示,或命令行执行file -i poems.json查看。
5.2 生僻字变成「□」:码位缺失与显示问题
现象:某首宋词里有生僻字,屏幕上显示为方块或问号,JSON 文件里对应的位置显示为\ufffd替换字符。
原因:分层有两个。第一层是数据源本身在转码时就把生僻字替换成了\ufffd,也就是说原始数据已经损坏,无法恢复;第二层是 JSON 里存了正确的 Unicode 码位,但操作系统或浏览器字体缺少对应字形,显示不出来。
解决:先区分是哪一层。用 Python 读取后打印字符的repr()或ord(),如果是0xFFFD,说明数据源头就丢了字形,这个只能回原数据源重新校对;如果码位正常,比如是0x2070E之类的扩展区汉字,那是字体问题,换一个支持扩展区的字体就能显示。做 Web 端展示时,建议给诗词正文设置一个覆盖范围广的字体栈,否则用户手机上大概率看到一堆方块。这个坑最容易在古籍和异体字多的文本里出现,统计生僻字覆盖率时建议单独拉一份清单看看。
5.3 同一首诗出现两遍:去重的判断依据
现象:搜索「静夜思」,返回两条记录,标题、作者、正文完全一致,但 id 不同。
原因:数据聚合自多个来源,整理者做合并时按「标题+作者」去重,但同一个标题下可能有不同版本的诗文,合并时把不同来源的重复条目都保留了。我见过有人会把同一首诗的「通行本」和「别集本」都收进去,字句有细微差异,这种不算严格重复,但用户搜索时看到两条差不多的记录会困惑。
解决:判断重复不能只看标题,要按「标题+作者+正文第一句」联合判断,正文首句通常能区分版本差异。去重时保留更完整的那条——对比两个条目的段落数、有无注释、有无译文,字段全的优先。如果数据量大,脚本里用一个 set 存指纹,指纹是作者/标题/正文第一句的拼接,遍历时第一次出现放进 set,后续重复直接跳过,跑一遍几万条也就几秒钟。去重后的数据建议另存一份,不要直接覆盖原始文件,后续排查问题还要用原始数据做对照。
5.4 注释和赏析混在正文里:字段污染
现象:展示正文时,某首诗的paragraphs数组里混进了「(此处缺字)」「编者注:……」之类的说明性文字,渲染出来很突兀。
原因:整理者在从网页或影印古籍提取文本时,把原文之外的编者注、校勘记也一并收进了正文数组,没有单独归属到notes字段。这在古籍电子化数据里尤其常见,因为网页上的原文和注释经常没有明确分隔标签。
解决:先用脚本筛查正文数组里包含「注」「编者」「校」等标识性字符的条目,人工抽看几条,确认是不是混入。逐条自动清洗容易误伤,比如诗词本身可能带有「自注」性质的语句,判断时要注意上下文。稳妥的做法是:把疑似混入的条目单独导出,人工或半自动判断,清洗后放入notes字段,而不是直接删除。如果量级很大,考虑只清洗展示层——渲染正文时按行过滤掉以括号或「编者」开头的行,保留原始数据不动,这样至少不影响用户阅读。
5.5 简体版把「里」和「裏」搞混了
现象:某首宋词原文里的「裏」(意为内、中)被转成了「里」,读起来语义变了,比如「梦裏」变成「梦里」;另一首里原本就是「里」(乡里、邻里),被误转成「裏」。
原因:这是简繁转换的经典错误。机器转换时按词频概率猜测,无法根据上下文判断哪个字才符合原意。诗词作为高度凝练的文言文本,这种错字对语义影响不小,尤其涉及意象的理解。
解决:如果数据集本身是繁体就最理想,尽量避免二次转换;如果只有简体版,做分析前先挑一批常见易错字(里/裏、发/髮、后/後、斗/鬥)检查一遍。修复时要逐条人工确认,因为有的是原文如此,有的确实是转换错误,批量替换一定会把对的那部分也替换掉。这个坑没有捷径,唯一的「后悔药」是保留原始版本,修复另存,不要原地覆盖。如果你要做的是诗词赏析方向的产品,建议尽量找存有繁体的版本或注明底本的数据集,减少这层风险。
6. 把它变成你自己的小工具:离线诗词查询与随机一诗
数据用顺手之后,可以做一个常用的小工具:随机一诗。这个功能在诗词类应用里几乎是标配,很多产品放在首页作为「每日一诗」。实现起来很简单,但有一些细节能让它更好用。
// random-poem.js const fs = require('fs'); const poems = JSON.parse(fs.readFileSync('./poems.min.json', 'utf-8')); function getRandomPoem(excludeId) { // 排除指定 id,避免连续两次抽到同一首 let pool = poems; if (excludeId) { pool = poems.filter(p => p.id !== excludeId); } const idx = Math.floor(Math.random() * pool.length); return pool[idx]; } let last = null; function nextPoem() { const poem = getRandomPoem(last ? last.id : null); last = poem; return poem; } // 用一次 console.log(nextPoem());这个脚本的核心是Math.random()取随机下标,配合excludeId避免连续重复。更细的做法是给随机函数加一个「权重」参数,比如想让五言绝句更容易出现,可以在选中的诗里再按体裁加权一次;但对大多数场景,均匀随机已经够用。跑成服务的话,可以把这个函数包一层setInterval,每天 0 点自动换一首,缓存在内存里,用户请求时直接返回当天的那首。
这类工具的进阶方向是沉淀「个人读诗记录」:把每次随机到的诗按日期存进本地 JSON 或 SQLite,时间长了就形成一份自己的阅读轨迹。我习惯在存记录时带上id而不是标题,这样即使将来数据更新、标题或正文有修订,记录依然能关联到正确的条目。这个做法的好处是数据可追溯,也方便将来做「读了多少天」「读过哪些诗人」的统计。
最后说一个个人教训。我早期用过一份从某网站导出整理的诗词数据,没做字段校对就上了展示页,结果一部分诗的朝代错位,用户按朝代浏览时看到李白出现在宋代,被反馈了好几次。后来重建数据加载层时,我把「先验证、后使用」写进了流程,数据入库前必跑统计脚本,朝代分布、作者频次、正文缺失三个指标都正常才进入下一步。诗词数据不等于普通 JSON,它的正确性直接关系到文化内容的可靠性,这份谨慎值得留着。希望这个技术方向和落地路径能帮到你,也让你的项目少走一段我走过的弯路。
本文还有配套的精品资源,点击获取