
简介全唐诗数据集为一份整理好的中文古诗数据库文件包含诗人与诗作两张数据表适合从事中国古典文学研究、数据分析或自然语言处理练习的读者使用。资源以zip压缩包发布内部共3个文件包括可导入MySQL的SQL脚本、说明文档以及Jupyter Notebook演示脚本整体大小约5.71MB结构简洁清晰解压后即可按文档指引完成建库与导入。数据中保留诗人ID、姓名、诗题及诗句等核心字段Notebook脚本还提供了基于SQL的分组统计示例可快速找出创作数量排名前十的唐代诗人为后续的文本挖掘、词频统计或可视化分析提供了干净的数据起点。该资源已有471人学习下载适合希望用真实古籍数据练习数据库操作、SQL查询或入门中文文本分析的开发者与学习者能够省去自行爬取与清洗的繁琐步骤直接聚焦业务分析与模型构建。1. 全唐诗数据集.zip先搞清楚包里装的到底是不是《全唐诗》搜索“全唐诗数据集.zip”的人通常不是想找电子书而是想拿一份能直接喂给脚本的语料库。我见过有人从网盘拖下来就急着解压折腾一下午才发现文件是 GBK 编码说明文档是繁体诗题和正文经常连成一行统计出来的诗作数量比公开记载的四万多首少了一大截。这个压缩包的价值不在 zip 本身而在它能不能变成“按诗人、按卷、按诗句可查询”的可靠数据底子。这篇文章按解压、验证、清洗、检索、排错、进阶这条路径把全唐诗数据集讲透适合正在做数字人文研究、古诗文语料处理以及刚入门 NLP 文本清洗的开发者。2. 从 zip 到可用语料解压前体检、CRC 校验与目录快照2.1 用 unzip 预览压缩包解压前先看清目录结构与文件数量拿到全唐诗数据集.zip我一般不会立刻双击解压。压缩包的中心目录记录了每个条目的文件名、大小、时间和 CRC 值用unzip -l可以无副作用地读取这份清单。Linux 和 macOS 直接执行下面的命令Windows 上如果装了 7-Zip 或 WSL同样用法。PowerShell 的 Expand-Archive 没有单独预览的能力解压前看不到压缩包内部结构拿来体检大体积数据集并不合适。unzip -l 全唐诗数据集.zip | head -80 unzip -l 全唐诗数据集.zip | tail -20head 看开头tail 看结尾目的是观察文件名规律和最后一个文件是否完整。重点看三件事扩展名是什么目录层级是按“卷”还是按“诗人”组织的有没有 README、索引或字段说明文件。如果压缩包里全是裸 txt没有说明文档就说明后面要用数据本身的规律来推断诗人和卷号。再看一眼文件总量unzip -l 全唐诗数据集.zip | awk NR3 {print $4} | grep -c /$ # 目录数 unzip -l 全唐诗数据集.zip | awk NR3 {print $4} | grep -vc /$ # 文件数awk 的NR3跳过 unzip 输出的表头和分隔线grep -c /$统计以斜杠结尾的目录条目。全唐诗常见整理版可能一卷一文件也可能按诗人合并成几十个大 txt。如果只有一两个 txt那多半是“全文合并版”清洗规则和一卷一文件完全不同。这个判断直接影响后面的脚本怎么写。2.2 用 zipfile 做逐个文件 CRC 校验避免解压到一半才报 EOCD 错下载得到的 zip 在传输中损坏非常常见。手动解压时见到invalid zip archive: could not find eocd或者导入资源时看到failed to copy spatial iop zip本质都是中央目录尾块丢失或文件不完整。出现这种报错不要尝试“修复”优先重新下载。为了避免解压到一半才发现问题我习惯先跑一个校验脚本用 Python 标准库的 zipfile 逐条检查 CRC。import zipfile from pathlib import Path def verify_zip(zip_path: str) - None: with zipfile.ZipFile(zip_path, r) as zf: infos zf.infolist() total len(infos) n_files sum(1 for i in infos if not i.is_dir()) n_dirs total - n_files total_size sum(i.file_size for i in infos) # testzip() 会逐个读取文件并比对 CRC-32返回第一个损坏文件名 bad zf.testzip() print(f条目总数: {total}, 目录: {n_dirs}, 文件: {n_files}) print(f解压后预估占用: {total_size / 1024 / 1024:.2f} MB) print(fCRC 校验第一个损坏文件: {bad if bad else 无}) verify_zip(全唐诗数据集.zip)testzip()从 zip 里逐个解压并校验 CRC返回第一个坏文件的名字。相比图形界面解压到一半才弹窗它能在一两分钟内告诉你整个包是否可信这是后面所有工作成立的前提。注意infolist()读取的是 central directory如果连这个都读不出来说明 EOCD 已经丢失直接走重新下载流程不要再浪费时间。2.3 解压后做一次目录快照把“内容地图”留好校验通过后正式解压并做一次目录快照。这个快照是后面清洗时对照进度的依据也是排查“文件是否被误删”的后悔药。mkdir -p 全唐诗_clean/raw_data unzip -q 全唐诗数据集.zip -d 全唐诗_clean/raw_data find 全唐诗_clean/raw_data -type f file_list.txt du -sh 全唐诗_clean/raw_data wc -l file_list.txt-q是安静模式避免刷屏-d指定解压目标目录。find把全部文件路径写入 file_list.txt后面清洗脚本每处理完一个文件就可以拿这份清单核对。du -sh看实际占用和前面 Python 算出的 file_size 对比如果相差巨大很可能磁盘写入出了问题。这一步做完压缩包阶段才算真正收尾。3. 编码问题怎么破用编码探测先洗出能读的全唐诗 txt3.1 乱码来自哪里UTF-8、GBK 与文件名的三种错位全唐诗文本资料跨度很大常见整理版里老版本多用 GBK 或 GB18030新版本普遍是 UTF-8。所谓乱码本质上就是编码错位。打开 txt 看到“鍏ㄥ攞璇”是 UTF-8 字节被 GBK 解释看到“锟斤拷”是 GBK 内容被转成 UTF-8 时留下的替换字符。还有一种更隐蔽的错位发生在解压工具读取 zip 内部文件名时文件名本身是 GBKWindows 资源管理器却按 UTF-8 解码解压出来目录名直接变成乱码。判断单个文件编码的快捷方式是看二进制头file -i 全唐诗卷001.txt xxd 全唐诗卷001.txt | head -2file -i输出 MIME 和 charsetxxd看头两行字节。UTF-8 无 BOM 的文本前面只有普通 ASCII 字节GBK 中文则明显是高位字节序列看到\ufeff开头的则是带 BOM 的 UTF-8。这些细节先确认不要急着写清洗规则。批量处理时我一般先写一个探测函数把每个文件的编码类型先跑出来。from pathlib import Path def sniff_encoding(path: Path) - str: data path.read_bytes()[:4096] for enc in (utf-8, gbk, utf-16): try: data.decode(enc) return enc except UnicodeDecodeError: continue return unknown只取前 4096 字节是为了减少大文件 IO 开销优先尝试 UTF-8再试 GBK。短文本有时用 GBK 解码也能“碰巧通过”所以这只是初筛更可靠的做法是同时统计解码成功率比如把 4096 字节切成一字节一字节判断可打印比例。对全唐诗这种生僻字多的语料我建议以“能解出多少有效汉字”为准而不是只看有没有报错。3.2 把 GBK 文本统一转成 UTF-8一个可复用的批量转换脚本确定编码后统一转成 UTF-8 无 BOM。这一步不修改原文件而是输出到新目录。保留原始素材非常关键因为后面清洗规则改坏了还能从原文件重新来过。import codecs from pathlib import Path def convert_tree(src_dir: Path, dst_dir: Path, src_enc: str gbk) - None: 把 src_dir 下所有 txt 从 src_enc 转成 UTF-8写入 dst_dir。 for src_path in src_dir.rglob(*.txt): rel src_path.relative_to(src_dir) dst_path dst_dir / rel dst_path.parent.mkdir(parentsTrue, exist_okTrue) with codecs.open(src_path, r, encodingsrc_enc, errorsstrict) as f: text f.read() with codecs.open(dst_path, w, encodingutf-8, errorsstrict) as f: f.write(text) print(fconverted: {rel})rglob(*.txt)递归找所有 txterrorsstrict保证遇到无法解码的字节立刻报错而不是用替换字符掩盖问题。如果转换到一半抛 UnicodeDecodeError说明这个文件不是 GBK把它单独拎出来用 3.1 的探测器再看。转换完成后抽样打开 3 到 5 个文件确认没有“锟斤拷”这类历史遗留替换字符再进行下一步清洗。3.3 清洗的第一刀去掉 BOM、全角空格和页眉页脚编码统一后先做一层轻量清洗。BOM 是最容易被忽视的坑带 BOM 的 UTF-8 文件喂给 Pandas 或分词工具第一个字段名会被读成“\ufeff题目”很多看起来莫名其妙的报错都从这来。import re def clean_line(line: str) - str: # 去掉 BOM、零宽字符和全角空格 line line.replace(\ufeff, ).replace(\u200b, ).strip() line re.sub(r[\u3000\xa0], , line) return line\u3000是中文全角空格\xa0是不换行空格在古籍文本里经常混入。strip()去掉首尾空白。页眉页脚如“钦定全唐诗卷九百”这一行体型和普通诗句很像用行级规则去删容易误伤常见做法是先不做等按块切分全唐诗时再根据上下文处理。清洗原则是“先能读再结构化”一步到位往往会把有效信息一起删掉。4. 清洗后的语料查询文本检索与统计的双重验证4.1 构建一个 jsonl 语料表每首诗一行是最省心的存法清洗之后最值得做的一步是把 txt 文本结构化成 jsonl每条记录对应一首诗。字段至少包含juan卷、author作者、title题目、body正文和source来源文件。一张表一张表地设计比反复按正则切 txt 更可靠。不同整理版的换行规律不一样但最常见的是“空行分段每段一首诗”。下面这个脚本按空行分块再把块内首行当候选题目其余当正文适用于结构规整的全唐诗整理版。import re import json from pathlib import Path BLOCK_SPLIT re.compile(r\n\s*\n) JUAN_HEAD re.compile(r卷\s*[一二三四五六七八九十百零]) def parse_txt(text: str, source: str): current_juan 未标卷 records [] for block in BLOCK_SPLIT.split(text): block block.strip() if not block: continue lines [ln.strip() for ln in block.splitlines() if ln.strip()] if not lines: continue # 卷标题行更新当前卷号不当作诗 if JUAN_HEAD.match(lines[0]): current_juan lines[0] continue # 常见整理版块内第一行为诗题第一行以下为正文 title lines[0] body .join(lines[1:]) records.append({ juan: current_juan, title: title, body: body, source: source, }) return records这个脚本的关键假设是“诗题独立一行”。如果你的数据集把诗题和正文放在同一行则需要用“短行优先”策略抽候选题目而不是直接取第一行。records里的juan不带数字只保留“卷九百”这样的原始形态是为了后续统计时能看到原书分卷逻辑。把不同文件得到的记录合并写成一个tang.jsonl每行一条 JSON后面对接 Pandas、Elasticsearch 或自建索引都很方便。4.2 全文检索的小模块支持按诗人、题目与诗句子串搜jsonl 建好之后写一个轻量检索类。全唐诗规模在几万首的量级Python 全量线性扫描已经足够快不必一上来就接数据库。重点是把“诗人”“题目”“正文”三类查询分开。import json class TangSearch: def __init__(self, jsonl_path: str): self.records [ json.loads(line) for line in open(jsonl_path, encodingutf-8) if line.strip() ] def by_author(self, name: str): return [r for r in self.records if r.get(author) and name in r[author]] def by_title(self, keyword: str): return [r for r in self.records if keyword in r[title]] def by_text(self, keyword: str): return [r for r in self.records if keyword in r[body]]这套实现没有建索引每次都做全量扫描但胜在逻辑直白。检索时注意繁体简体同时试比如“长恨歌”和“長恨歌”是同一个作品如果只搜简体会漏掉一部分异体字记录。更稳妥的做法是在构建 jsonl 时额外加一个title_norm字段统一把繁体转简体再把这个字段作为检索入口。4.3 统计诗人与诗作数量用自己的脚本给数据集“验明正身”语料库是否接近完整的《全唐诗》不是靠感觉判断的。公开信息普遍记载《全唐诗》有诗四万余首、作者两千余人。如果统计出来只有一万首、几百个作者那这个 zip 大概率是选本或残本后面做的风格分析、字频统计都不可信。from collections import Counter with open(tang.jsonl, encodingutf-8) as f: records [json.loads(line) for line in f if line.strip()] cnt_author Counter(r[author] for r in records if r.get(author)) cnt_juan Counter(r[juan] for r in records) print(诗作总数:, len(records)) print(诗人数量:, len(cnt_author)) print(卷数:, len(cnt_juan)) print(诗人 Top10:, cnt_author.most_common(10))盯着 Top10 看有没有明显异常。比如“李白”“杜甫”出现在前列是正常的如果出现“卷九百”或“目录”这类非人名说明清洗脚本把标题行误当成了作者。另一个有效校验是查长诗《长恨歌》全文约 840 字按 title 搜出来之后打印len(body)如果只有一两百字说明正文被截断分层结构出了问题。5. 全唐诗数据集常见问题排查5 个必踩的坑现象、原因与解决5.1 zip 报 EOCD 错误或者 CRC 校验失败现象双击压缩包解压到一半报错提示could not find eocd或invalid zip archivePython 里打开直接抛 BadZipFile。原因下载不完整、网盘中转丢尾块极少数是压缩时就已经损坏。解决不要尝试用修复工具补 zip优先回到来源重新下载下载后第一时间跑 2.2 的 CRC 校验脚本把“拿到 zip 先验证”养成习惯。一个完整但 CRC 报错的包解压后大概率也有坏文件不值得继续投入。5.2 Windows 上中文文件名解压后乱码现象全唐诗数据集里明明写着“全唐诗卷001.txt”解压出来却变成“é‡ç³–”或“鍏ㄥ攞璇”文件内容反而是对的。原因zip 内部文件名字段是 GBK而图形化解压工具默认按 UTF-8 解码。解决不要依赖系统解压用 Python zipfile 手动处理。注意 Python 对未带 UTF-8 标记的文件名默认按 cp437 解码这层要先回退成原始字节再重新解码。import zipfile with zipfile.ZipFile(全唐诗数据集.zip, r) as zf: for info in zf.infolist(): raw info.filename.encode(cp437) # 回退到原始字节 name None for enc in (utf-8, gbk): try: name raw.decode(enc) break except UnicodeDecodeError: continue if name is None: name info.filename print(name)这段脚本先按 cp437 还原 zipfile 默认解码时丢失的原始字节再尝试 UTF-8 和 GBK。跑一遍把真实文件名打印出来确认哪个编码正确再决定整个目录怎么落盘。这是处理老数据集的常见做法遇到一次就记住了。5.3 诗题和正文连成一行统计题名时数量不对现象按诗题统计数量比诗作总数少很多打印 jsonl 时发现有些记录的 title 特别长明显混进了正文。原因整理版把诗题和正文放在同一行或者没有统一换行符。解决不要只靠“短行是题目”这一条规则容易把一字诗和残句误判。常见做法是先用“短行 以赠、送、题、咏、和、酬等惯用题词开头”的双重条件抽候选题目再抽样 200 条人工确认。规则宁可保守让抽取不到的记录保持 title 为空也不要硬切。5.4 同一首诗在不同卷中重复出现现象统计作者时数量虚高去重后数量又骤降。原因《全唐诗》里存在互见诗同一作品被收进不同诗人卷下异文也可能有差别。解决不要直接删重复记录。在 jsonl 里保留source和juan字段统计时以(author, title)作为去重键但正文对比要保留多版本。去重前先看 title 里有没有“又”“再”这类字那是同卷重收的标志直接去重会把有效信息丢掉。我一般把去重结果单独存成tang_dedup.jsonl原文件不动。5.5 生僻字、异体字和缺字让统计结果失真现象字频统计里“閒”“閑”“”混在一起同一首诗在不同版本里用字不一致。原因原书使用旧字形或扫描 OCR 时产生缺字Unicode 里这些字码位不同肉眼看着像程序不认。解决统一做 Unicode NFC 规范化把“异体字映射表”单独维护成字典遇到\ufffd替换字符必须记录到 error 表不能默默丢弃。映射表的目标是统计可读而不是把古籍改成现代字形所以原始正文不要覆盖。6. 进阶跑一次字频分析用最少代码验证全唐诗数据集质量结构化的 tang.jsonl 能做的事很多我建议先跑字频因为它对数据质量最敏感。全唐诗高频字集中在“人、山、月、风、云、日、水、春”这类意象字如果 Top30 里出现了“的、了、吗”说明正文混进了非诗句文本如果第一位是“卷”或“第”说明卷标题没有切干净。from collections import Counter import re import json cnt Counter() for line in open(tang.jsonl, encodingutf-8): rec json.loads(line) body re.sub(r[\s。、“”《》], , rec[body]) cnt.update(body) for ch, n in cnt.most_common(30): print(ch, n)正则只去掉标点和空白不改动字形。跑完先看有没有\ufffd有就回到 5.5 的 error 表把该文件重新清洗。接着把李白和杜甫的高频字各统计一份按作者分组对比用字差异这比直接画词云更能验证清洗质量。我每次换一个新数据集都会先打印字频 Top30 肉眼核对一遍再做下游分析。一次因为忘记去 BOM导致第一个字永远是“\ufeff”差点把错误结论写进笔记后来所有清洗脚本都强制带验证步骤。这个习惯救了我很多次希望帮到你。本文还有配套的精品资源点击获取