ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

诗词JSON数据集实战:从压缩包到可查询语料库的完整流程

诗词JSON数据集实战:从压缩包到可查询语料库的完整流程 简介这份资源是 chinese-poetry 开源项目的完整离线压缩包面向从事中文文本处理、数据挖掘与自然语言学习的研究者和开发者。原项目托管于 GitHub直接 clone 或下载 zip 速度较慢此包由作者提前下载整理方便需要处理诗词数据的朋友直接取用。压缩包共 1371 个文件约 84.85MB其中 1339 个 json 文件是核心数据涵盖唐诗、宋诗、元曲及作者信息等结构化内容另有少量 md 说明、png 配图、yml 配置、py 脚本、js 与 db 文件便于快速搭建本地数据环境。目前已有 1074 人学习下载。拿到后可直接读取 json 做分词、检索、统计或模型训练省去网络等待与格式转换的麻烦适合作为中文古典文学数据项目的起步素材。1. 诗词 JSON 数据集从压缩包到可查询语料库的第一步手里拿到一个「中国诗词大全 json 版」的压缩包很多人第一反应是解压看看里面有什么然后就被一堆.json文件劝退——文件多、字段不熟、编码偶尔还闹脾气。这个资源解决的核心问题很具体把散落在古籍、影印本、网络辑录里的诗词文本整理成结构化、可程序化检索的 JSON 数据。它适合谁做诗词类 App 或小程序的开发者、想用 SQL 或 Pandas 做文学统计的分析者、需要给大模型喂中文古典语料的研究者以及单纯想离线保存一份干净诗词库的爱好者。JSON 的好处是跨语言、易解析、字段自描述比 CSV 更能表达嵌套结构比如一首诗带多个版本、多个注释。但「能打开」和「能用好」之间隔着字段设计、编码、查询效率三道坎这篇就按我实际拆包、入库、查询的顺序走一遍。2. 拆开压缩包先看结构文件命名、字段设计与编码2.1 典型目录布局与文件命名规律这类诗词 JSON 压缩包解压后常见做法是按朝代或作者分文件而不是一个巨型 JSON。我拿到的包结构大致是这样# 解压后先看目录树别急着写代码 unzip chinese-poetry-json.zip -d poetry_data cd poetry_data find . -maxdepth 2 -type f -name *.json | head -20输出往往类似./tang/poet.tang.0.json ./tang/poet.tang.1000.json ./tang/poet.tang.2000.json ./song/poet.song.0.json ./song/poet.song.1000.json ./authors.tang.json ./authors.song.json这里有两个关键信息第一poet.tang.0.json这种带数字后缀的通常是分片文件每片装固定条数常见 1000 条目的是避免单个 JSON 过大导致解析内存爆掉第二authors.tang.json是作者索引和诗文件分开存。分片设计对后续批量入库很友好但如果你直接json.load整个目录会重复加载作者信息得先想清楚合并策略。2.2 单条诗词的字段含义与嵌套结构打开一个分片文件取第一条看字段import json with open(tang/poet.tang.0.json, r, encodingutf-8) as f: poems json.load(f) print(json.dumps(poems[0], ensure_asciiFalse, indent2))典型输出{ author: 李世民, paragraphs: [ 秦川雄帝宅函谷壮皇居。, 绮殿千寻起离宫百雉馀。 ], title: 帝京篇十首 一, id: 8b7a3c1e-... }字段不多但每个都有坑。paragraphs是数组不是单个字符串因为古诗有换行和分段id可能是 UUID 也可能是自增数字取决于数据来源title里常带「其一」「其二」这种组诗标记检索时需要额外处理。作者字段author是纯文本没有单独的作者 ID 关联这意味着你想按作者聚合得自己建映射表。常见做法是先解析authors.*.json把作者名和简介、生卒年对应起来再和诗文件做 join。2.3 编码与转义为什么你的 json.load 会报错血泪经验第一条这类数据集里偶尔混入非 UTF-8 编码的文件或者 JSON 字符串里带了未转义的控制字符。直接json.load会抛UnicodeDecodeError或json.decoder.JSONDecodeError。稳妥的读法是这样import json def safe_load(path): # 先按 utf-8 读失败再尝试 gbk最后用 errorsreplace 兜底 for enc in (utf-8, gbk): try: with open(path, r, encodingenc) as f: return json.load(f) except (UnicodeDecodeError, json.JSONDecodeError): continue with open(path, r, encodingutf-8, errorsreplace) as f: return json.load(f)参数说明errorsreplace会把无法解码的字节替换成保证程序不崩但会丢字。所以更稳的做法是先用chardet探测编码再决定用哪种。如果只是做统计丢几个生僻字影响不大如果要做全文检索就得逐文件检查编码把异常文件单独拎出来修。提示解压后先跑一遍file -i *.json看编码声明比事后 debug 省事。3. 把 JSON 灌进数据库SQLite 与 MySQL 的字段映射3.1 建表从嵌套数组到关系表JSON 适合传输不适合直接查询。要把诗词做成可检索的库第一步是设计表结构。最简方案两张表poems和authors。-- SQLite 建表MySQL 把 AUTOINCREMENT 换成 AUTO_INCREMENT CREATE TABLE authors ( author_name TEXT PRIMARY KEY, dynasty TEXT, intro TEXT ); CREATE TABLE poems ( poem_id TEXT PRIMARY KEY, title TEXT NOT NULL, author_name TEXT, content TEXT, -- paragraphs 用换行符拼接 dynasty TEXT, FOREIGN KEY (author_name) REFERENCES authors(author_name) ); CREATE INDEX idx_poems_author ON poems(author_name); CREATE INDEX idx_poems_title ON poems(title);content字段把paragraphs数组用\n拼成一段文本方便后续LIKE或全文索引。poem_id直接用 JSON 里的id如果缺失就自己生成 UUID。注意外键在 SQLite 里默认不强制需要PRAGMA foreign_keys ON;。3.2 批量导入脚本分片读取与事务提交不要一条一条INSERT几千条就能让你等到怀疑人生。用事务批量提交import json import sqlite3 import glob conn sqlite3.connect(poetry.db) cur conn.cursor() cur.execute(PRAGMA foreign_keys ON;) # 先导入作者 for path in glob.glob(authors.*.json): with open(path, r, encodingutf-8) as f: authors json.load(f) cur.executemany( INSERT OR IGNORE INTO authors (author_name, dynasty, intro) VALUES (?, ?, ?), [(a.get(name), a.get(dynasty), a.get(desc, )) for a in authors] ) # 再导入诗按分片批量提交 for path in glob.glob(tang/poet.tang.*.json) glob.glob(song/poet.song.*.json): with open(path, r, encodingutf-8) as f: poems json.load(f) rows [] for p in poems: rows.append(( p.get(id), p.get(title, ), p.get(author, ), \n.join(p.get(paragraphs, [])), 唐 if tang in path else 宋 )) cur.executemany( INSERT OR IGNORE INTO poems (poem_id, title, author_name, content, dynasty) VALUES (?, ?, ?, ?, ?), rows ) conn.commit() # 每个分片提交一次避免大事务锁库 conn.close()逻辑说明INSERT OR IGNORE防止重复导入同一分片时主键冲突每个分片提交一次平衡速度和内存。参数上glob的顺序不保证但导入顺序不影响最终结果。如果数据量超过百万条SQLite 建议关掉synchronous和journal_mode调优但那是另一个话题。3.3 查询验证从「床前明月光」反查作者与朝代导入完先跑几条查询确认数据完整-- 查标题含「静夜思」的诗 SELECT title, author_name, dynasty FROM poems WHERE title LIKE %静夜思%; -- 查李白所有诗的数量 SELECT COUNT(*) FROM poems WHERE author_name 李白; -- 全文模糊匹配数据量大时慢仅验证用 SELECT title, author_name FROM poems WHERE content LIKE %床前明月光%;如果COUNT(*)是 0先检查作者名是否有空格或异体字如果LIKE查不到可能是content拼接时丢了换行或标点。常见坑是 JSON 里paragraphs的标点用了全角而查询用了半角导致匹配失败。4. 避坑与排查编码、重复、字段缺失的实战记录4.1 现象解压后文件名乱码json 打不开原因压缩包在 Windows 下打包文件名用了 GBK 编码而 Linux/macOS 默认按 UTF-8 解析。解决用unzip -O GBK指定编码解压或者用 Python 的zipfile手动处理文件名。unzip -O GBK chinese-poetry-json.zip -d poetry_data如果已经解压乱了用convmv -f gbk -t utf-8 -r --notest poetry_data/批量重命名。4.2 现象导入数据库后诗数量比 JSON 文件里少原因INSERT OR IGNORE把poem_id重复的记录跳过了而不同分片里可能存在相同id数据源合并时的遗留问题。解决先查重复 IDSELECT poem_id, COUNT(*) FROM poems GROUP BY poem_id HAVING COUNT(*) 1;如果重复量不大可以接受如果要保留所有版本把主键改成自增poem_id降为普通字段。4.3 现象按作者查询时同一作者出现「李白」和「李白 」两个结果原因JSON 里作者名带了尾随空格或全角空格。解决导入前统一strip()并把全角空格替换成半角author p.get(author, ).strip().replace(\u3000, )4.4 现象json.load报Expecting , delimiter原因JSON 文件里某条记录的paragraphs数组末尾多了逗号或者字符串里混入了未转义的引号。解决用json.loads逐条解析定位到具体行或者用jq先格式化检查jq . tang/poet.tang.0.json /dev/nulljq报错会直接指出行号比 Python 的报错更直观。4.5 现象SQLite 查询全文慢到无法忍受原因LIKE %关键词%无法走索引百万级数据全表扫描。解决建 FTS5 虚拟表CREATE VIRTUAL TABLE poems_fts USING fts5(title, content, contentpoems, content_rowidrowid); INSERT INTO poems_fts(rowid, title, content) SELECT rowid, title, content FROM poems;之后用SELECT * FROM poems_fts WHERE poems_fts MATCH 明月;查询速度提升明显。注意 FTS5 对中文分词支持有限默认按字符切分短词查询够用长句建议配合tokenizeunicode61。5. 进阶技巧用 Python 做诗词统计与 JSON 导出5.1 统计高频字与朝代分布数据入库后最常被问的就是「哪个字出现最多」「唐宋诗数量对比」。用 Pandas 几行搞定import sqlite3 import pandas as pd from collections import Counter conn sqlite3.connect(poetry.db) df pd.read_sql(SELECT dynasty, content FROM poems, conn) # 朝代分布 print(df[dynasty].value_counts()) # 高频字去掉标点和空白 all_text .join(df[content].tolist()) chars [c for c in all_text if \u4e00 c \u9fff] print(Counter(chars).most_common(20))参数说明\u4e00到\u9fff是 CJK 统一汉字区间过滤掉标点、数字和换行。如果要做词频得先分词常见做法是jieba但古诗分词效果一般按字统计更稳。5.2 导出子集为新的 JSON按作者或朝代筛选有时候你只需要李白或全唐诗导出成独立 JSON 方便分发import json import sqlite3 conn sqlite3.connect(poetry.db) cur conn.cursor() cur.execute(SELECT title, author_name, content FROM poems WHERE author_name ?, (李白,)) rows cur.fetchall() result [] for title, author, content in rows: result.append({ title: title, author: author, paragraphs: content.split(\n) }) with open(libai.json, w, encodingutf-8) as f: json.dump(result, f, ensure_asciiFalse, indent2)ensure_asciiFalse保证中文直接输出不转成\uXXXXindent2方便人眼查看但文件会变大分发时可以用separators(,, :)压缩。5.3 验证数据完整性的三个检查点导出后别急着用跑三个检查第一随机抽 10 条人工核对标题、作者、内容是否对应第二统计paragraphs为空的记录数正常应该为 0第三检查title里是否有乱码字符。我一般会写一个validate.py每次导出后强制走一遍import json with open(libai.json, r, encodingutf-8) as f: data json.load(f) empty [d for d in data if not d[paragraphs]] print(f总条数: {len(data)}, 空内容: {len(empty)}) assert len(empty) 0, 存在空内容记录从那以后我每次导出子集都先跑这个脚本确认没有空字段和乱码才敢往项目里塞。希望帮到你。本文还有配套的精品资源点击获取
返回列表