简介: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.width和page.height用于后续判断表格区域坐标。如果前五页输出里出现大量无关的版权页、目录页,后面解析正文页码范围时要手动跳过。
2.2 观察条目格式,圈定正则边界
确定有文本层后,找到一页典型的词汇正文,观察每条术语的结构。常见的 CFA 词汇 PDF 条目格式有两种:
- 纯文本型:每行一个术语,如
abnormal returns 异常收益 - 表格型:两列或三列,术语、词性、释义分列,但用
extract_text()提取后很可能变成「abnormal returns 异常收益 n. 超过预期收益的部分」
确认结构后,为每条词汇规划字段。我一般会建这样一张表,字段顺序固定,方便后续导入 SQLite 和 Anki:
| 字段名 | 类型 | 说明 | 示例 |
|---|---|---|---|
term_en | TEXT | 英文术语原文,保留大小写 | Abnormal Returns |
term_cn | TEXT | 中文释义主体 | 异常收益 |
POS | TEXT | 词性标注,可为空 | n. |
definition | TEXT | 详细中文解释 | 实际收益与预期收益之差 |
subject | TEXT | 所属学科分类 | 组合管理 |
source_page | INTEGER | 在 PDF 中的页码 | 127 |
字段规划的意义在于:不同用途对字段的粒度要求不同。做间隔重复(spaced repetition)时,POS和definition可以组合成填空题卡面;做知识图谱时,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 会按页面默认配置检测表格线,返回一个二维列表,每行是单元格内容,空单元格是None。pdf.pages[5:]是切片语法,从第 6 页开始处理,跳过目录和版权页;具体起始页要看第一步打印结果。这里把source_page直接写进 CSV,后面回溯出处时不用重新解析整个文件。
3. 清洗三座大山:换行符、英中粘连、词性噪声
3.1 把提取结果从「能看」变成「能查」
PDF 提取出的文本经常带着上一行结尾的连字符和\n,最典型的情况是abnormal\nreturns被拆成两行。对词汇表来说,术语本身是短文本,跨行断词一般只出现在释义里。清洗策略按严重程度分三层:
- 去除页眉页脚和页码行
- 合并被换行符截断的英文短语
- 分离英中释义,处理词性标注
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 表格导出后常见的多空格分隔;split后parts[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);字段里frequency和last_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》。
本文还有配套的精品资源,点击获取