news 2026/9/18 16:23:38

用Python将CFA词汇PDF转为结构化词库:解析清洗与SQLite存储实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Python将CFA词汇PDF转为结构化词库:解析清洗与SQLite存储实践

简介:CFA核心词汇.pdf是一份面向CFA考生及金融从业者的专业术语整理文档,系统收录金融、会计、投资、证券、保险等领域的核心词汇,内容覆盖从基础概念到实务应用。文档按字母顺序编排,每个词条均附中文对照与详细解释,例如Accelerated Depreciation(加快折旧)、Accounts Receivable(应收账款)、Agency Problem(代理问题)等,既有概念定义又有应用示例,便于理解记忆和考前速查。资源为单个PDF文件,体积仅3.29MB,携带便捷且排版清晰,适合在电脑或移动设备上随时查阅。目前已有730人学习使用,说明对备考金融证书或提升专业英语确有帮助。这些词汇不只对应考试,也常见于财报、研报和实际业务场景,掌握后能显著加快英文资料的阅读速度,并为后续学习CFA其他科目打下扎实基础,切实提升备考效率。

1. 拿到 CFA 词汇 PDF 之后,工程师先做文本清洗

一份《CFA核心词汇.pdf》在大多数考生手里是背诵材料,但在工程视角里,它是一个未经治理的文本数据源。CFA 官方教材本身用词非常克制,三千多个核心术语分布在道德、数量、经济学、财报分析、公司金融、权益、固收、衍生品、另类投资和组合管理十个学科里,PDF 格式意味着你没法直接grep、没法条件筛选、没法按章节做词频统计。第一次用 Python 打开这类 PDF 时常见的挫败感是:表格边界错乱、换行符把术语拦腰截断、英中对照列对不齐。

把这份 PDF 转成可查询的 CSV 或 SQLite 库并不是为了炫技,而是为了两个实际目标:一是能在命令行里按学科、按首字母、按词频过滤词汇;二是能二次加工成 Anki 卡组、自测问卷或知识图谱。本文按「提取 → 清洗 → 结构化 → 复用」这条路径展开,所有代码建议直接跑一遍,你会看到从 PDF 里的半结构化文本到干净术语表的完整过程。适合想用程序化方式吃透 CFA 词汇的开发者,也适合做 NLP 语料预处理的人参考——PDF 解析的坑在这份资料里非常典型。

2. 定位 PDF 正文结构,规划词汇字段

2.1 先判断 PDF 是文本层还是扫描件

处理任何 PDF 的第一步永远是确认它有没有文本层。用pdfplumber打开文件后,如果page.extract_text()返回的是空字符串,那说明这是一份图片型扫描件,需要 OCR。CFA 词汇表这类资料通常由 Word 或 LaTeX 导出,文本层大概率存在,但不同出版社生成的 PDF 结构差异很大,有的词汇表是双栏排版,有的是单栏逐条列出。

import pdfplumber with pdfplumber.open("CFA核心词汇.pdf") as pdf: for i, page in enumerate(pdf.pages[:5]): text = page.extract_text() print(f"=== 第 {i+1} 页 ===") print(text[:500]) print("--- 页宽:", page.width, "页高:", page.height)

这段代码先打印前五页内容,确认三个关键信息:文本是否完整、是单栏还是双栏、页眉页脚是否混进正文。参数说明:pdf.pages是按页迭代的列表对象,page.extract_text()返回该页全部文本,page.widthpage.height用于后续判断表格区域坐标。如果前五页输出里出现大量无关的版权页、目录页,后面解析正文页码范围时要手动跳过。

2.2 观察条目格式,圈定正则边界

确定有文本层后,找到一页典型的词汇正文,观察每条术语的结构。常见的 CFA 词汇 PDF 条目格式有两种:

  • 纯文本型:每行一个术语,如abnormal returns 异常收益
  • 表格型:两列或三列,术语、词性、释义分列,但用extract_text()提取后很可能变成「abnormal returns 异常收益 n. 超过预期收益的部分」

确认结构后,为每条词汇规划字段。我一般会建这样一张表,字段顺序固定,方便后续导入 SQLite 和 Anki:

字段名类型说明示例
term_enTEXT英文术语原文,保留大小写Abnormal Returns
term_cnTEXT中文释义主体异常收益
POSTEXT词性标注,可为空n.
definitionTEXT详细中文解释实际收益与预期收益之差
subjectTEXT所属学科分类组合管理
source_pageINTEGER在 PDF 中的页码127

字段规划的意义在于:不同用途对字段的粒度要求不同。做间隔重复(spaced repetition)时,POSdefinition可以组合成填空题卡面;做知识图谱时,subject字段是节点聚类的核心维度。如果一开始只草草存成「英文+中文」二元组,后面再做分类就得回头翻 PDF。

2.3 用 pdfplumber 精确抽取表格型词汇

如果词汇页是表格排版,extract_text()的裸文本容易把同一行不同列的内容粘连在一起。更稳妥的方式是用page.extract_table()按表格线切分:

import pdfplumber import csv with pdfplumber.open("CFA核心词汇.pdf") as pdf: rows_all = [] for page in pdf.pages[5:]: # 假设前5页是目录 table = page.extract_table() if table: rows_all.extend(table) with open("cfa_terms_raw.csv", "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow(["term_en", "term_cn", "definition", "source_page"]) for row in rows_all: writer.writerow(row)

参数说明:extract_table()不传参数时,pdfplumber 会按页面默认配置检测表格线,返回一个二维列表,每行是单元格内容,空单元格是Nonepdf.pages[5:]是切片语法,从第 6 页开始处理,跳过目录和版权页;具体起始页要看第一步打印结果。这里把source_page直接写进 CSV,后面回溯出处时不用重新解析整个文件。

3. 清洗三座大山:换行符、英中粘连、词性噪声

3.1 把提取结果从「能看」变成「能查」

PDF 提取出的文本经常带着上一行结尾的连字符和\n,最典型的情况是abnormal\nreturns被拆成两行。对词汇表来说,术语本身是短文本,跨行断词一般只出现在释义里。清洗策略按严重程度分三层:

  1. 去除页眉页脚和页码行
  2. 合并被换行符截断的英文短语
  3. 分离英中释义,处理词性标注
import re import pandas as pd df = pd.read_csv("cfa_terms_raw.csv") def clean_term(en: str) -> str: en = re.sub(r"\s+", " ", en) # 压缩空白和换行 en = en.replace("- ", "") # 去掉行尾连字符,如 "abnormal-" + "returns" en = en.strip() return en def split_cn_definition(cell: str): parts = re.split(r"\s{2,}|(?<=[\u4e00-\u9fff])\s+(?=[a-zA-Z])", cell) cn = parts[0] if parts else "" definition = " ".join(parts[1:]) if len(parts) > 1 else "" return cn.strip(), definition.strip() df["term_en"] = df["term_en"].apply(clean_term) df[["term_cn", "definition"]] = df["definition"].apply( lambda x: pd.Series(split_cn_definition(str(x))) )

这里第二个正则值得展开:(?<=[\u4e00-\u9fff])\s+(?=[a-zA-Z])是一个零宽断言匹配,它只匹配「中文字符后紧跟空格再紧跟英文字母」的位置,用来把「异常收益 n. 实际收益与预期之差」切分成中文核心词和后面的英文释义。参数说明:\s{2,}匹配两个以上连续空格,处理 PDF 表格导出后常见的多空格分隔;splitparts[0]一定是最纯的中文术语,parts[1:]是剩余释义,如果原文没有词性和详细解释,definition就是空字符串,后续导入数据库时可填NULL

3.2 识别并剔除「伪词条」

清洗完基本格式后,还有一个隐蔽问题:PDF 词汇表里除了真正的术语,还会混入章节标题、表格注脚、页码和目录条目。比如「数量分析方法」「Equity」这类是学科章节名,不是术语。用规则过滤:

def is_valid_entry(en, cn): if not en or not cn: return False if re.fullmatch(r"[A-Za-z\s\-']+", en) is None: return False # 英文术语不应含中文字符或数字开头 if len(en) < 2 or len(cn) < 1: return False return True df_filtered = df[df.apply(lambda r: is_valid_entry(r["term_en"], r["term_cn"]), axis=1)] df_filtered = df_filtered.drop_duplicates(subset=["term_en"])

re.fullmatch(r"[A-Za-z\s\-']+", en)强制整个英文术语只包含字母、空格、连字符和撇号。像「ROE = Return on Equity」这种带等号的字符串会被过滤掉,你可能会觉得误伤,但在 CFA 词汇表里「ROE =」通常是公式说明而不是术语本身。drop_duplicates(subset=["term_en"])保留第一次出现的术语,后续重复出现的同名术语(比如在不同章节都有解释)会被丢弃。

3.3 学科分类的自动推断

原始 PDF 如果没有提供分类栏位,可以用人工划分的章节页范围来做。每本 CFA 词汇 PDF 的术语排列顺序基本和教材章节一致,道德在开头,组合管理在结尾。切分方法是在解析循环里记录每个术语的source_page,再按页号打标签:

subject_ranges = [ (5, 40, "Ethics"), # 道德 (41, 70, "Quantitative"),# 数量 (71, 120, "Economics"), # 经济学 (121, 200, "FR&A"), # 财报分析 # 实际页数按你的 PDF 调整 ] def assign_subject(page): for start, end, name in subject_ranges: if start <= page <= end: return name return "Uncategorized" df_filtered["subject"] = df_filtered["source_page"].apply(assign_subject)

这段代码的隐含假设是:词汇表在 PDF 内部按学科章节连续排列。如果发现某学科的术语散落在互不相邻的页区间,就在subject_ranges里加多个区间,同一个学科可以出现两次,循环会依次匹配。这个方法的准确率主要取决于你对原书目录的熟悉程度,但属于成本最低的自动分类方案。

4. 把清洗结果库化:SQLite 存储与向量化索引

4.1 建表与幂等导入

对几百上千条术语,CSV 够用,但要支持条件查询、按学科聚合和后续扩展字段,SQLite 更合适。建表时把term_en设为唯一索引,重复导入不会产生脏数据:

CREATE TABLE IF NOT EXISTS cfa_vocab ( id INTEGER PRIMARY KEY AUTOINCREMENT, term_en TEXT UNIQUE NOT NULL, term_cn TEXT NOT NULL, POS TEXT, definition TEXT, subject TEXT DEFAULT 'Uncategorized', source_page INTEGER, frequency INTEGER DEFAULT 0, last_reviewed DATE ); CREATE INDEX IF NOT EXISTS idx_subject ON cfa_vocab(subject);

字段里frequencylast_reviewed是后来加的,用于记录词条在教材语料里出现的次数和个人复习进度。UNIQUE约束在词汇表清洗完后可能引发插入冲突,所以导入逻辑要写成「存在则跳过,不存在则插入」:

import sqlite3 conn = sqlite3.connect("cfa_vocab.db") cur = conn.cursor() for _, row in df_filtered.iterrows(): cur.execute(""" INSERT INTO cfa_vocab (term_en, term_cn, POS, definition, subject, source_page) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(term_en) DO NOTHING """, (row["term_en"], row["term_cn"], row.get("POS"), row["definition"], row["subject"], row["source_page"])) conn.commit() conn.close()

ON CONFLICT(term_en) DO NOTHING是 SQLite 3.24.0 之后的 UPSERT 语法,比先 SELECT 再 INSERT 快得多,而且保留了表里已有词的复习进度字段,不会被重新导入的清数据覆盖。

4.2 用全文检索做模糊查词

很多场景下用户输入的不是严格术语,比如「什么是异常收益」,你需要匹配term_cn里的子串。SQLite 自带的LIKE在几百条数据上没问题,但术语库扩展到几千条、释义也有几百字时,建 FTS5 虚拟表更专业:

CREATE VIRTUAL TABLE IF NOT EXISTS cfa_fts USING fts5( term_en, term_cn, definition, content='cfa_vocab', content_rowid='id' ); INSERT INTO cfa_fts(rowid, term_en, term_cn, definition) SELECT id, term_en, term_cn, definition FROM cfa_vocab;

查询时注意 FTS5 默认按分词匹配,英文按空格切分,中文要用unicode61的默认规则,它对汉字是逐字切分还是整体切分取决于 SQLite 编译配置。稳妥做法是用LIKE兜底:

search_term = "异常" cursor.execute(""" SELECT term_en, term_cn, subject FROM cfa_vocab WHERE term_en LIKE ? OR term_cn LIKE ? """, (f"%{search_term}%", f"%{search_term}%"))

这里没走 FTS5 而是用LIKE,原因是中文子串匹配在 FTS5 里对配置敏感,与其在分词器上调半天,不如直接在原始表上跑模糊查询。数据量万条以内,LIKE性能毫无压力。

4.3 生成 Anki 导入用的 TSV

词库的价值在复用。从 SQLite 生成 Anki 卡组时,Anki 桌面版支持从 TSV 文件导入,每行是一张卡。卡面是英文术语,背面是中文释义和学科标签:

with open("cfa_anki.tsv", "w", encoding="utf-8") as f: for row in conn.execute(""" SELECT term_en, term_cn, POS, subject FROM cfa_vocab ORDER BY subject, term_en """): en, cn, pos, subject = row back = f"{cn}{' [' + pos + ']' if pos else ''}({subject})" f.write(f"{en}\t{back}\n")

TSV 里每行两列,第一列卡片正面,第二列背面。这个文件的换行符在 Windows 上打开 Excel 可能不兼容,Anki 导入时无碍。做完这一步,你可以把同一个库导出成 JSON 喂给前端做自测页,也可以只保留 SQLite 文件作为后续知识图谱构建的数据源。

5. 三个联动场景:Anki 热词加权、术语共现网络、回填教材页码

5.1 统计教材语料的术语频次,回写权重

这是进阶玩法里比较实用的一种。把 CFA 官方教材或 Notes 的文本拉下来,逐条统计核心词汇在教材中出现的频次,写回 SQLite 的frequency字段。高频术语优先复习,低频术语降低权重:

import collections import re def count_terms(text_path: str, conn): with open(text_path, encoding="utf-8") as f: corpus = f.read() counters = collections.Counter() for (en,) in conn.execute("SELECT term_en FROM cfa_vocab"): pattern = r"\b" + re.escape(en) + r"\b" counters[en] = len(re.findall(pattern, corpus, flags=re.IGNORECASE)) for en, freq in counters.items(): conn.execute("UPDATE cfa_vocab SET frequency=? WHERE term_en=?", (freq, en)) conn.commit()

\b...\b是单词边界,确保匹配的是完整单词而不是子串,IGNORECASE让「return」同时匹配「Return」和「RETURN」。这里有个坑:像「risk」这种短词,在语料里几乎无处不在,分开统计后频率曲线会严重右偏,建议最后把频次做 log 归一化再用于排序。

5.2 术语共现网络:找出跨学科枢纽词

CFA 各学科不是孤立的,「duration」同时出现在固收和组合管理里,「beta」横跨权益和衍生品。利用 SQLite 里的subject字段可以快速看一个术语横跨几个学科:

SELECT term_en, COUNT(DISTINCT subject) AS subj_cnt FROM cfa_vocab GROUP BY term_en HAVING subj_cnt > 1 ORDER BY subj_cnt DESC;

这个查询输出的就是跨学科枢纽词。把它导出成节点列表,喂给 NetworkX 画图,得到的是知识结构图而不仅仅是词表。如果你愿意多做一步,把每个术语的definition拿去做 Jieba 分词,再把分词结果和所有术语取交集,「术语共现矩阵」就出来了。

5.3 把source_page映射成带超链接的复习页面

最后一个使用技巧是把 SQLite 里的source_page回填到生成好的 HTML 复习页面里,点击术语可以跳回原 PDF 对应页:

for row in conn.execute("SELECT term_en, source_page FROM cfa_vocab"): link = f"cfa.pdf#page={row['source_page']}" print(f'<a href="{link}">{row["term_en"]}</a>')

浏览器打开带#page=N的本地 PDF 链接时,Chrome 和 Edge 会直接跳到指定页面,这比在 PDF 阅读器里手翻页码快得多。生成完整个 HTML 后,你就拥有一个带定位、带学科标签、带复习状态的词汇工作台,而这一切的起点只是那份看似静态的《CFA核心词汇.pdf》。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 16:23:36

3DMAX建模思维断层:从物理规律到职业级决策链

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 16:21:13

Flutter+HarmonyOS打造高校新生报到系统实战

1. 项目背景与核心价值高校新生报到季向来是校园管理中最繁忙的时段之一。去年参与某985高校数字化改造时&#xff0c;我亲眼目睹了这样的场景&#xff1a;行政老师手写登记表堆满三张办公桌&#xff0c;家长排队绕教学楼两圈&#xff0c;十几个学生志愿者拿着喇叭维持秩序。这…

作者头像 李华
网站建设 2026/9/18 16:19:28

ASP.NET 学生选课系统高并发扣减、防超发与 ViewState 加固

简介&#xff1a;面向计算机相关专业学生与.NET Web开发入门者的一份毕业设计文档&#xff0c;主题为基于ASP.NET的学生选课系统设计与实现。文档以学士学位论文结构展开&#xff0c;先介绍选课系统的背景、目的与实现功能&#xff0c;再说明SQL Server 2000数据库和Visual Stu…

作者头像 李华
网站建设 2026/9/18 16:18:24

Unity快速次表面散射SSS:基于曲率与厚度的移动端皮肤渲染

做了几年Unity渲染&#xff0c;皮肤材质一直是个绕不开的坎。角色模型明明做得挺精细&#xff0c;一打光就像上了层清漆的塑料&#xff0c;尤其是耳廓、鼻翼、指缝这些透着薄肉的部位&#xff0c;黑得死死的&#xff0c;怎么调都缺那股“活人气”。其实问题不在贴图&#xff0c…

作者头像 李华
网站建设 2026/9/18 16:15:55

用Python解析docx题库:从三支一扶试题到结构化数据

简介&#xff1a;2019年广西柳州市三支一扶考试补录试题及答案解析&#xff0c;是一份面向三支一扶备考群体的真题演练资料&#xff0c;尤其适合报考广西柳州地区岗位、需要熟悉当地笔试题型和难度的考生。资源包内共1个docx文档&#xff0c;文档约46页&#xff0c;整体大小仅6…

作者头像 李华