简介这份非 PDF 版的《牛津英语词典》以 Excel 和 SQL 两种格式打包面向需要把词典数据融入学习或开发流程的人群包括学生、教师、语言爱好者及软件开发者。Excel 表可直接打开支持查找、编辑、排序与筛选适合制作个人生词本、课堂讲义或按词性/词频做专题分析SQL 数据库则具备结构化存储与高效检索能力可结合 Python、Java 等语言调用用于构建桌面、Web、移动端的翻译或背词工具。资源包共 2 个文件分别提供表格版与数据库版整体仅 2.63MB数据组织清晰、便于二次利用。已有 3075 人学习下载。通过这一份数据既能以表格方式快速翻阅和整理单词也能将数据库接入程序实现自动化查询、批量导入导出甚至在线词典服务兼顾日常学习与项目开发是一份实用、轻量且扩展空间大的词典数据资源。1. 为什么词典必须离开 PDFExcel 与 SQL 是查词与二次开发的唯一出路做翻译、备课或语言研究的人最怕的就是手头有一部牛津英语词典的 PDF想查一个词要滚动半天想统计某个词缀的出现频率更是无从下手。PDF 本质上是“给人眼看”的排版产物词条、音标、释义、例句全都混在版式层里机器读不到干净的字段边界。牛津英语词典翻译的 Excel、SQL 版本非 PDF 版本做的就是把这本词典从逐页翻阅的形态改造成一行一个词条、一列一个字段的结构化数据——词头在哪、音标在哪、词性是什么、第几个义项对应哪条翻译全部清清楚楚。这类需求在翻译团队、外语教学机构、词典工具开发者里非常常见值得自己动手做因为外部现成数据源往往有版权、格式或更新滞后的问题而自己转换出来即可控、又能二次加工。本文就把从源文本到 Excel、再到 SQLite 的完整路径走一遍包括列结构、表设计、参数选择和实际的坑。2. 源数据准备拿到词典文本后的格式侦察与字段切分把词典变成 Excel 或 SQL 版本第一步不是写转换脚本而是先搞清楚你手里的源文件是什么形态。这一步做扎实了后面所有规则都是水到渠成。2.1 三种常见源格式的识别与取舍我处理过的词典源数据主要有三类它们的解析难度和字段完整度差别很大选错格式会让自己多走好几天的弯路。第一类是 HTML 页面转储也就是从在线词典逐词条抓下来的页面。优点是词条内部的音标、词性、释义常有对应的标签或类名定位字段时不用靠猜缺点是被抓页面数量大了之后标签结构会有版本差异而且抓下来的是整个网页里面还有导航栏、页脚等干扰信息需要额外清洗。第二类是 XML 结构化文件这是最理想的源——词头、音标、词性、义项之间都有明确的元素边界解析时按标签提取即可几乎没有歧义但这种源的获取门槛比较高。第三类是纯文本 TXT通常来自 OCR 识别或手工整理最麻烦词条之间可能只有一个空行相隔音标和释义挤在同一行全角半角混用空格数量也不固定。拿到 TXT 先别急着写解析脚本做一次“格式侦察”是必须的。我的习惯是先跑三条命令确认文件的基本盘。# format_recon.sh # 统计总行数判断是每词一行还是多行一条 wc -l dictionary_source.txt # 看前 80 行确认词条分隔与字段形态 head -n 80 dictionary_source.txt # 用 file 命令识别编码避免后面在 Python 里因编码报错 file dictionary_source.txt这三条命令里wc -l的作用是快速判断文件规模——如果 10 万行左右通常是每词条一行如果几十万行说明词条块会跨行分隔规则要按块处理。head -n 80是看真实结构的底线操作很多坑在前 80 行就能提前暴露比如某个词条里例句中出现了和词头格式一样的单词这种干扰必须在后面写分隔正则时提前防范。file命令输出的编码信息决定后面用open()打开文件时传给encoding参数的值这一步错了后面的解析脚本会连环报错。如果是 HTML 原始转储建议先用工具把标签剥离成“近似 TXT”的形态再进入下一步。我一般用html.parser或正则把span、div这类标签去掉只保留可见文本。剥完之后要立刻抽查十来个词条确认剥除过程没有把音标里的字符误伤掉——有些词典排版会把音标写在尖括号里比如ˈæbəndən这种标签在剥除时会被当成 HTML 标签删除导致音标字段只剩一对空括号。2.2 词条解析的最小可行脚本拿到形态相对规整的 TXT 或拉平后的 HTML 文本就可以写解析脚本了。我的做法是先按词条分隔符把整篇文本切成块再对每个块做字段提取。# parse_entries.py import re import json from pathlib import Path RAW Path(./source.txt) OUT Path(./entries.json) # 词条分隔行独立成行的词头 空行作为边界 # 词头是英文字母开头可含连字符和空格如 abandon 或 high-level SEP re.compile(r^([A-Za-z][A-Za-z\-\s])$\n\n, re.MULTILINE) def split_entries(text): # 用分隔正则找所有词头的位置再按位置切出每个词条的剩余正文 matches list(SEP.finditer(text)) entries [] for i, m in enumerate(matches): head m.group(1).strip() body_start m.end() body_end matches[i 1].start() if i 1 len(matches) else len(text) body text[body_start:body_end] entries.append({headword: head, body: body}) return entries def parse_body(body): # 提取音标通常夹在 /.../ 斜杠之间 pron re.search(r/([^/])/, body) # 提取词性标注adj. / adv. / v. / n. 等白名单形式 pos_m re.search(r\b(adj|adv|v|n|prep|pron|conj|interj)\., body) # 提取按数字编号的义项义项编号如 1. 2. 3. senses re.findall(r\b(\d)\.\s*(.?)(?\b\d\.\s*|$), body, re.DOTALL) return { phonetic: pron.group(1) if pron else , pos: pos_m.group(1) if pos_m else , senses: [{order: int(s[0]), text: s[1].strip()} for s in senses], } if __name__ __main__: text RAW.read_text(encodingutf-8) entries split_entries(text) for ent in entries: ent.update(parse_body(ent.pop(body))) OUT.write_text(json.dumps(entries, ensure_asciiFalse, indent2), encodingutf-8) print(f解析完成共 {len(entries)} 个词条)这段脚本的核心思路是“先粗切再细提”。split_entries按“独立成行 空行结尾”的词头模式把文本切成块正则里的^...$配合MULTILINE保证词头必须顶格这样例句中出现的单词即使和词头拼写一样也不会被误判成新词条。parse_body里音标提取用的是/.../正斜杠包裹因为词典排版惯例里音标几乎总是夹在斜杠中词性提取用白名单匹配避免把释义里的词误判成词性。几个关键的参数细节如果你的源数据词头含数字或带姓氏缩写要扩进SEP的正则字符集里比如加上.和,音标如果出现变体字符如ˈ、ə源文件必须保持 UTF-8 编码完整否则phonetic字段会丢字符。json.dump里ensure_asciiFalse是必须的否则中文释义会被转成\uXXXX形式的 ASCII 转义序列后续读起来非常难受。切块之后先跑一次print(len(entries))看看词条数是否符合预期如果数量比预想少很多优先检查分隔正则是否把某些词头开头的词条块吞掉了。2.3 音标、词性与释义的字段切分规则解析逻辑里最油滑的部分不是正则本身而是切分规则的优先级先定词条边界再定音标最后看词性和释义。音标提取是否成功取决于源数据是否把音标放在词头正后方——有的词典把音标放在词性之后有的放在释义之后甚至有的词条根本没标音标。我在处理时会把“缺音标”当作合法状态而不是报错否则脚本会在第一个缺音标词条处崩溃。词性提取最常见的问题是“一个词条对应多个词性”。比如 “abandon” 同时是动词和名词源数据里会写成 “v. n.” 或 “v., n.”。此时要么取第一个词性存成一行要么把一个词条拆成多个义项、每个义项带上自己的词性。我一般选择后者因为后面 SQL 版本里更需要在“动词释义”和“名词释义”之间做索引和筛选混在一起会让WHERE pos v这种查询直接失效。释义切割要防两件事一是义项编号可能不是纯数字有的源数据用 “1a.” “1b.” 这种字母后缀区分子义项二是释义文本里会出现 “e.g.” 这类带点带空格的缩写如果单纯按点号切分会把例句截断。我的做法是义项编号正则放宽允许\d[a-z]?这种形态同时释义和例句的切分只在明确的分隔符处进行不在句号处切。如果源数据里释义已经按词性分组比如 “v. 1. ... 2. ... ” 和 “n. 1. ...” 分开写我会在解析时把词性和义项编号组合成sense_key比如v-1、n-2。这样后续 Excel 的筛选和 SQL 的查询都能精确定位到单个义项而不是只到词条级别。切分完成后把entries.json打开抽样看 20 个词条确认音标是否完整、词性是否落在白名单内、释义里是否还有多余的制表符或连续空格。这一步看仔细了后面 Excel 和 SQL 阶段基本可以一路顺风。3. 把词条装进 Excel列结构、去重筛选与十万行不卡的表格细节词典装进 Excel 之后最值钱的体验是“筛选”和“定位”而不是像 PDF 里从头翻到尾。这一章主要解决列怎么摆、转换脚本怎么写以及几个让人用得舒服的细节。3.1 列结构设计词头、音标、词性、释义、义项序号分列Excel 版本的目标是让人能横向看清楚一个词条的全部信息。我的列结构通常是六列headword、phonetic、pos、sense_key、definition、example。其中sense_key就是上一节提到的v-1、n-2这种组合它的价值在于让多义词拆开后依然能看出归属顺序也方便后续在 Excel 里做“筛选所有动词”这种操作。如果源数据里每个词条只有一个主要义项可以缩成四列但只要是完整的牛津词典数据多义词一定是常态所以不要为了表格看起来简洁把多个义项挤进同一格。那种做法的直接后果是筛选时能搜到词头但想筛“所有动词义项”就完全做不到。列结构一旦定下来尽量不要改因为后面 SQL 的三张表结构会跟着它走改列等于整个导入脚本推倒重来。3.2 用 pandas 把 JSON 词条写成 Excel 文件从entries.json到dictionary.xlsx的转换脚本不长但有两个关键点一是保持 UTF-8 编码完整二是写入时显式指定列顺序不依赖字典的插入顺序。# json_to_excel.py import pandas as pd from pathlib import Path entries pd.read_json(Path(./entries.json), encodingutf-8) rows [] for ent in entries.to_dict(records): head ent[headword] pho ent.get(phonetic, ) pos ent.get(pos, ) for sense in ent.get(senses, []): if not sense.get(text, ).strip(): continue rows.append({ headword: head, phonetic: pho, pos: pos, sense_key: f{pos}-{sense[order]}, definition: sense[text], example: , # 如果源数据里有例句字段在这里填入 }) df pd.DataFrame(rows, columns[ headword, phonetic, pos, sense_key, definition, example ]) # 用 openpyxl 引擎写入 xlsx不写 row index df.to_excel(dictionary.xlsx, indexFalse, engineopenpyxl) print(fExcel 写入完成共 {len(df)} 行)这段脚本的循环把“一个词条多个义项”的一对多结构拍平成表格行。sense_key在 Python 里拼好而不是在 Excel 里用公式拼是为了避免电子表格在后续筛选和排序时重算公式导致卡顿或引用错误。to_excel的indexFalse是必须的否则 pandas 默认加一列行号后续筛选时这列会碍事。参数说明engineopenpyxl是写入 xlsx 格式的标准引擎如果数据量超过 10 万行to_excel会明显变慢此时我建议先df.to_csv(dictionary.csv, indexFalse)导出成 CSV再在 Excel 里手动导入。CSV 对内存占用更小而且导入导出都方便不会因为 Excel 打开大 xlsx 而卡死。3.3 让筛选、模糊查询真正可用的三个细节第一个细节是“冻结首行 自动筛选”。这两个操作可以用 openpyxl 的 API 在脚本里直接设置好否则使用者滚动五百行之后就找不到列名了。没有自动筛选的表格等于把筛选功能钉死在手动菜单里使用体验会差很多。# 在生成后的 workbook 上追加设置接在 to_excel 之后 from openpyxl import load_workbook wb load_workbook(dictionary.xlsx) ws wb.active ws.freeze_panes A2 # 冻结首行滚动时列名始终可见 ws.auto_filter.ref ws.dimensions # 给整张表加自动筛选 wb.save(dictionary.xlsx)这里freeze_panesA2表示从第二行开始滚动首行固定auto_filter.ref直接用ws.dimensions拿到表格实际范围这样只要数据更新筛选范围会跟着走。需要注意的是load_workbook会重新读写一遍文件所以这段代码要放在to_excel之后执行另外若文件特别大重载一次会有额外耗时但一般完全可接受。第二个细节是“清理释义里的全角和不可见空格”。英文释义中的弯引号、中文释义里的全角引号和括号、以及可能的\u00a0不间断空格都会让筛选和 SQL 字符串匹配变成玄学——肉眼看着是同一个词查询就是匹配不上。我的处理是在解析阶段统一把全角引号转成半角、全角空格转半角、连续多个空格压成一个。这一步成本极低却能省掉后面数不清的排错时间。第三个细节是“给音标列选对字体”。IPA 音标字符在 Excel 默认字体下会显示成方框。把phonetic列的字体设成 “Arial Unicode MS” 或 “Lucida Sans Unicode” 之后音标字符才能正常显示。加上这一条能避免 80% 的“你这个表是不是坏了”的反馈。手动设置一次即可或者在转换脚本里用 openpyxl 的Font(nameArial Unicode MS)指定该列单元格的字体。4. SQL 版本建表、索引与高频查询写法Excel 适合人眼看SQL 适合程序调用。如果你要做翻译记忆库、词频统计或者给应用提供词典接口SQL 版本是更稳的底座。这一章从表结构设计讲到导入流程再给出高频查询的写法。4.1 表结构设计词条表、义项表与例句表为什么必须分开词典数据是天然的一对多结构一个词头下面有多个义项一个义项下面可能还有多个例句。如果强行把所有信息塞进一张宽表里同一个词头会重复几十次数据冗余不说索引体积也会膨胀得很快。以 “set” 这类多义词为例一个词头可能有三十多个义项宽表里就会重复三十多次词头和音标存储浪费严重查询“abandon 的全部动词释义”时还需要额外的去重逻辑。因此我一般拆成三张表关系清晰、冗余可控。表名字段说明entriesid, headword, phonetic一个词头一条记录sensesid, entry_id, pos, sense_order, definition一个义项一条记录外键指向 entriesexamplesid, sense_id, example_text例句表按需保留拆表之后查“某个词头的全部释义”需要一次 JOIN比单表查询多写几个字符但对词典这种一对多关系极强的数据JOIN 的代价远小于冗余存储带来的索引膨胀。一张 20 万行、每行带长文本的词典宽表和一张 20 万词头 60 万义项的三表结构后者的查询性能明显更稳定尤其是在LIKE模糊搜索场景下。4.2 把 JSON 或 Excel 数据导入 SQLite 的完整流程导入逻辑不复杂但要注意插入顺序先插 entries 拿到entry_id再插 senses 拿到sense_id最后插 examples。反过来先插子表外键就指向了不存在的记录。# import_to_sqlite.py import sqlite3 import pandas as pd from pathlib import Path # 可以直接从第 3 章生成的 Excel 读也可以从 JSON 读 df pd.read_excel(dictionary.xlsx, engineopenpyxl) conn sqlite3.connect(oed_dict.db) cur conn.cursor() # 建表外键约束保证数据完整性 cur.executescript( DROP TABLE IF EXISTS examples; DROP TABLE IF EXISTS senses; DROP TABLE IF EXISTS entries; CREATE TABLE entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, headword TEXT NOT NULL, phonetic TEXT DEFAULT ); CREATE TABLE senses ( id INTEGER PRIMARY KEY AUTOINCREMENT, entry_id INTEGER NOT NULL, pos TEXT, sense_order INTEGER, definition TEXT, FOREIGN KEY(entry_id) REFERENCES entries(id) ); CREATE TABLE examples ( id INTEGER PRIMARY KEY AUTOINCREMENT, sense_id INTEGER NOT NULL, example_text TEXT, FOREIGN KEY(sense_id) REFERENCES senses(id) ); ) # 用字典做词头去重缓存保证每个词头只入 entries 一次 entries_cache {} for _, row in df.iterrows(): head row[headword] if head not in entries_cache: cur.execute( INSERT INTO entries (headword, phonetic) VALUES (?, ?), (head, row[phonetic] or ) ) entries_cache[head] cur.lastrowid eid entries_cache[head] cur.execute( INSERT INTO senses (entry_id, pos, sense_order, definition) VALUES (?, ?, ?, ?), (eid, row[pos] or , 1, row[definition]) ) conn.commit() conn.close() print(f导入完成共 {len(df)} 个义项)这段脚本里entries_cache用 Python 字典做查询层去重保证同一个词头只插入一次 entries而 senses 每次单独插入形成典型的一对多关系。cur.lastrowid在插入后立即取到新记录的id是 SQLite 获取自增主键的标准方式。外层iterrows()逐行迭代对一次性导入场景完全够用如果有百万级数据量建议改用executemany批量插入并分批 commit能显著减少磁盘 I/O。导入完成后SELECT COUNT(*) FROM entries;确认词头数没有缩水。如果发现远少于预期绝大多数原因是第 3 章 Excel 阶段去重逻辑把同形不同词的条目弄丢了——具体修法放到第 5 章避坑部分详细说。4.3 命中 90% 查询场景的 SQL 写法与索引建议词典查询最常做的三件事是精确查词、前缀模糊查、释义内关键词搜索。精确查询用即可但headword列必须建索引否则 20 万行全表扫描会让每次查询卡在几十毫秒以上。-- 精确查询查一个词的音标和全部义项 SELECT e.headword, e.phonetic, s.pos, s.sense_order, s.definition FROM entries e JOIN senses s ON s.entry_id e.id WHERE e.headword abandon ORDER BY s.pos, s.sense_order; -- 前缀模糊查询查以某个前缀开头的词 SELECT headword FROM entries WHERE headword LIKE abandon% ORDER BY headword; -- 释义内关键词搜索查释义中包含某个中文词的条目 SELECT e.headword, s.definition FROM entries e JOIN senses s ON s.entry_id e.id WHERE s.definition LIKE %放弃% LIMIT 20;索引建议方面entries.headword上建普通 B-tree 索引即可senses.entry_id上建外键索引加速 JOINsenses.pos如果经常单独按词性过滤也建一个。注意LIKE abandon%在前缀命中时可以用上普通索引但LIKE %放弃%这种中缀查询一定全表扫。如果经常搜释义里的关键词建议用 SQLite 的 FTS5 扩展为senses.definition建全文索引查询速度和LIKE不在一个量级。-- 建 FTS5 虚拟表用于全文检索只在 SQLite 中使用 CREATE VIRTUAL TABLE senses_fts USING fts5(definition, contentsenses); -- 查询示例 SELECT e.headword, s.definition FROM senses_fts fts JOIN senses s ON s.id fts.rowid JOIN entries e ON e.id s.entry_id WHERE fts.definition MATCH 放弃 LIMIT 20;参数说明FTS5 的contentsenses表示虚拟表内容来自senses表不额外复制数据MATCH是全文检索的匹配语法比LIKE快一个量级也支持中文分词按 unicode 分段。这里的代价是虚拟表需要额外占用空间但词典规模下完全可接受。5. 词典转换避坑指南编码、合并与数据丢失的五个典型翻车现场从源文本到 Excel/SQL这个过程中我踩过的坑不在少数。按“现象 → 原因 → 解决”的方式列出来希望能帮你避开同款陷阱。5.1 中文字符乱码根源是源文件的编码声明与实际不符现象解析脚本跑完导出的 Excel 里中文释义全部是乱码或者出现“锟斤拷”这类替换字符。原因源文件实际是 GBK 编码但脚本用 UTF-8 打开或者反过来源是 UTF-8脚本按 GBK 解码。更隐蔽的情况是文件带 BOMopen()默认参数没处理导致第一个字段前面多了\ufeff后续拼接时把整个字段都弄乱。解决在读取源文件时先用第 2 章的file命令确认编码。如果输出显示UTF-8就用open(..., encodingutf-8-sig)——utf-8-sig会自动剥离 BOM。如果显示GBK就用encodinggbk并在读完后立即把字符串转成 UTF-8 再往下传。我一般会在脚本里写一个函数先探测再打开避免写死编码。5.2 词条合并时把词性不同的同形词弄丢了现象转换完成后词条总数对不上比如 “bank” 应该同时有“河岸”和“银行”两个独立条目结果只剩一条。原因去重逻辑只按headword判断忽略了词性维度。牛津词典这类工具书会把同形但不同词性、不同词源的词算作独立条目而我们做 Excel 去重时如果以词头作为唯一键后出现的记录会被前面同词头的记录覆盖。解决去重键改为(headword, pos)组合。第 3 章 Excel 阶段的rows.append前先判断(headword, pos)是否已存在SQL 阶段则给entries表加唯一索引(headword, pos)从数据库层面防止重复。注意这会导致 entries 表里同一词头出现多行但这才是词典数据的真实结构。5.3 释义里的符号噪声没清理全文检索怎么搜都搜不到现象SQL 里LIKE %放弃%明明应该有结果但就是查不到肉眼检查 Excel 又确实能看到“放弃”两个字。原因释义文本里混入了不间断空格\u00a0、全角空格、或零宽字符\u200b-\u200d。这些字符视觉上不可见或者看起来像个普通空格但在字符串匹配时它们是不同字符LIKE就无法命中。解决在解析阶段做一次文本规整化——把\u00a0替换成普通空格全角空格转半角用正则[\u200b-\u200d]剔除零宽字符再把连续多个空格压成一个。我建议把这段逻辑封装成一个normalize_text()函数在 JSON 和 Excel 两个阶段都调用保证数据一致性。5.4 JSON 多级转义处理不当导致解析失败或数据截断现象用json.load()读entries.json时直接报JSONDecodeError或者释义文本中间被莫名截断。原因源数据里的释义本身含有引号、换行符、反斜杠在构造 JSON 时没有正确转义或者转义了一层但读取时又解析了两次导致嵌套字符串里的反斜杠把后续内容“吃掉”了。解决写入 JSON 时用json.dump(..., ensure_asciiFalse, indent2)让系统自己处理转义读取时用json.load()不要手动拼接字符串。尤其不要在转为 JSON 之前用正则去人为转义引号很容易弄混转义层级导致原本合法的 JSON 文件变得不可读。5.5 把 Excel 当数据库用超过十万行后开始卡到怀疑人生现象xlsx 文件 50MB 以上双击打开要等近一分钟筛选一次卡十几秒甚至 Excel 直接提示“资源不足”。原因把释义和例句的长文本全部塞进一张 Excel 表行数超过 10 万后xlsx 的底层 XML 结构导致读写开销成倍增长。这不是电脑配置问题是工具选型问题——Excel 的适用边界就在几十万行以内而且长文本列是性能杀手。解决把 Excel 文件控制在 10 万行以内词条超过这个量就拆成按字母分区的多个 sheet或者把 Excel 当作“给人看”的出口主力查询交给 SQL。我的实践是 Excel 版保留全字段但只放常用列超长的例句列不放进 Excel只在 SQL 里保留这样文件大小和操作流畅度都能兼顾。6. 自己再做一步验证词典完整性的脚本与一个秒查命令做完 Excel 和 SQL 版本不要急着交付先用脚本验证一遍数据完整性。完整性的标准是词条数量合理、字段缺失率低、抽查正确率高。验证通过后再顺手做一个命令行点查工具日常使用就不再需要打开 Excel。我的验证习惯是三条 SQL查总量、查音标缺失率、随机抽词条比对原文。如果音标缺失率超过 1%就得回头检查解析规则是不是漏了一种音标格式。# validate.sh sqlite3 oed_dict.db SELECT COUNT(*) FROM entries; sqlite3 oed_dict.db SELECT COUNT(*) FROM entries WHERE phonetic ; # 随机抽 20 个词条看音标、词性、释义是否完整 sqlite3 -header -column oed_dict.db \ SELECT e.headword, e.phonetic, s.pos, s.definition FROM entries e JOIN senses s ON s.entry_id e.id ORDER BY RANDOM() LIMIT 20;命令行点查工具用 Python 写最短路径读 SQLite、打印词条全貌。关键技巧是查询参数用占位符传入不直接拼接字符串避免引号和百分号破坏查询语句。这个工具自己用方便也能被其他脚本调用来做批量查词。# lookup.py import sqlite3 import sys def lookup(word): conn sqlite3.connect(oed_dict.db) cur conn.cursor() cur.execute( SELECT e.headword, e.phonetic, s.pos, s.sense_order, s.definition FROM entries e JOIN senses s ON s.entry_id e.id WHERE e.headword ? ORDER BY s.pos, s.sense_order , (word,)) rows cur.fetchall() if not rows: print(f未找到: {word}) return for head, pho, pos, order, definition in rows: print(f[{head}] /{pho}/ ({pos}) {order}. {definition}) conn.close() if __name__ __main__: lookup(sys.argv[1].strip().lower())这条从源文本到 Excel/SQL、再到验证查询的完整链路我自己反复跑过多次最大的体会是词典转换这种事80% 的工作量在数据清洗和规则确认上真正写转换脚本的时间反而少。源数据质量参差不齐宁可多花一小时看原始文本的 80 行也别迷信一个万能正则。转换完成后把验证脚本留着未来换数据源的时候直接复用能省不少时间。希望这些经验能帮到你。本文还有配套的精品资源点击获取