十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Python实现考纲词汇PDF转Anki卡片,自动统计掌握率

Python实现考纲词汇PDF转Anki卡片,自动统计掌握率 简介北京成人本科学位英语考试大纲词汇总表是为成人本科学位英语考生整理的备考核心资料以单个PDF文件发布文件大小仅307KB。该考试重在检验成人高等教育学习者的英语综合应用能力而这份词表正对应考纲要求按A到Z顺序收录了abandon、ability、absolute、accept等基础高频词也收入accelerate、academic、accomplish等学术类和书面表达词汇每个单词配有中英文双语释义方便不同水平的成人学习者对照记忆和查漏补缺。除单词外表中还融入adjective、adverb、agreement、analysis等语法关键概念帮助考生理解句子结构与语法规则进而在语法填空和完形填空中减少失分对于阅读和听力考生也能借助这些词汇快速定位主旨、抓取细节提高理解速度与准确度。这份资料体量小使用方便目前已有260人浏览学习适合需要系统背词、巩固语法并提升综合语言能力的成人考生反复研读和自测。1. 为什么“大纲词汇总表.pdf”不是拿来背的而是拿来解析的拿到“北京成人本科学位英语考试大纲词汇总表.pdf”多数人会直接打开 PDF翻到词表页一边划线一边背。这个动作本身没有错但对 IT 从业者来说效率太低。这份文档真正值钱的是它定义好的“考纲词汇边界”和相对固定的词条排版一个词一行后面跟着音标或释义。这种结构一旦被抽成结构化数据就能做出去重、标记已掌握、批量生成 Anki 卡片、统计复习进度等一系列操作。反过来如果一直停留在 PDF 阅读器里检索只能靠 CtrlF复习只能靠眼睛翻页本质上还是在用处理普通文档的方式处理词汇数据。这篇文章要解决的就是一件事把“北京成人本科学位英语考试大纲词汇总表.pdf”从“能看的文档”变成“能跑的词汇数据源”。按抽取、清洗、生成卡片、进度复查四步走全部操作在本地完成普通 Windows 或 macOS 机器都能跑通。2. 用 PyMuPDF 把“大纲词汇总表.pdf”抽成结构化词条2.1 先判断 PDF 有没有文本层PDF 分两类。文本型 PDF 的文字本身就是可复制的字符用脚本直接提取即可扫描型 PDF 只有图片必须先过 OCR。判断方法很直接用 PyMuPDF 读一页看get_text()返回的长度。长度接近 0说明这页没有文字层。import fitz doc fitz.open(北京成人本科学位英语考试大纲词汇总表.pdf) for i in range(min(3, len(doc))): text doc[i].get_text(text) print(f第 {i 1} 页文本长度 {len(text)}) print(text[:80].replace(\n, ))fitz.open()打开本地文件路径不会发起任何外部请求。get_text(text)按页面阅读顺序返回纯文本len(text)是判断是否有文字层的核心指标。如果前三页长度都在几十以内大概率是扫描版跳到 2.4 节处理如果文本长度成百上千说明是文本型可以直接走下面的解析流程。2.2 词条行的三种常见排版形态考纲词汇表的排版不会只有一种。我处理过几类不同来源的 Word 转 PDF 词表常见形态如下你需要先抽取几页文本对照观察再决定规则排版形态示例抽取策略单词和注释在同一行abandon [əbændən] vt. 抛弃一行正则直接匹配单词单独一行释义在下一行abandon换行[əbændən] vt. 抛弃缓存上一行下一行作为释义合并两栏排版按行交错左栏单词、右栏释义文本层顺序乱按坐标排序后用(x0, y0)分组前两种情况占大多数本文的代码按这两种设计。两栏排版相对少见如果遇到用page.get_text(dict)取出每个字符的坐标按x0坐标分列重建行顺序原理不复杂但代码量会翻倍一般考试大纲词汇表很少用这种版式。2.3 抽取脚本行匹配加状态缓存import fitz import re doc fitz.open(北京成人本科学位英语考试大纲词汇总表.pdf) word_re re.compile(r^[A-Za-z][A-Za-z\-]*$) entry_re re.compile( r^(?Pword[A-Za-z][\w\-]*) r(?:\s*(?Pphonetic\[[^\]\n]*\]|/[^/\n]*/))? r(?:\s*(?Pmeaning\S.*))?$ ) records [] pending None for page in doc: for raw in page.get_text(text).splitlines(): line raw.strip() if not line: continue if re.fullmatch(r[\d\s], line): continue m entry_re.match(line) if m and (m.group(meaning) or m.group(phonetic)): records.append({ word: m.group(word), phonetic: m.group(phonetic) or , meaning: m.group(meaning) or , }) pending None elif word_re.match(line): pending line elif pending: records.append({ word: pending, phonetic: , meaning: line, }) pending None print(f共抽取 {len(records)} 条记录)entry_re有三个分组word匹配单词本体phonetic匹配方括号或斜杠包裹的音标并允许为空meaning匹配剩余释义部分。word_re只匹配“纯单词行”用于处理单词和释义分行的场景。pending变量作为状态缓存如果当前行是纯单词先不落库等下一行进来自动合并。re.fullmatch(r[\d\s], line)过滤页码和纯数字行避免把页脚当成词条。这段脚本跑完records就是初步的结构化词条。打印前 20 条检查一下如果发现把目录页、页眉也抽进来了增加过滤条件跳过包含“目录”“大纲”“考试”等标题词的行或者从正文页码开始遍历。2.4 扫描版 PDF 走 OCR 兜底如果确认 PDF 是图片扫描版就不能用文本抽取了。常见做法是安装paddleocr和paddlepaddle逐页识别后输出文本再回到 2.3 的正则逻辑处理。PaddleOCR 默认按检测框输出文字识别结果的顺序不见得和阅读顺序一致需要按box[0][1]即 y 坐标排序后再拼接行。from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) result ocr.ocr(page_001.png, clsTrue)use_angle_clsTrue让方向分类器处理旋转文本langch指定中英文混合识别。OCR 的精度受扫描质量和字体影响音标部分尤其容易出错。识别完成后把文本交给 2.3 的解析逻辑音标字段如果出现乱码先原样保留不要急着删后面清洗阶段再处理。3. 词条清洗与去重让一份考纲词汇只保留一份记录3.1 先统计重复词条再决定合并策略从 PDF 抽出来的数据大概率有脏数据。最常见的是同一个单词在不同页重复出现或者大小写不一致导致被当成两个词。先用 Counter 统计一遍心里有数再清洗。from collections import Counter words [r[word].strip().lower() for r in records] counter Counter(words) dups {w: n for w, n in counter.items() if n 1} print(f总数 {len(words)}去重后 {len(counter)}重复词条 {len(dups)} 个) print(list(dups.items())[:10])这里先把单词统一转小写再计数避免Abandon和abandon被当成两个词。但注意转小写只用于统计原始单词要保留一份因为部分专有名词在考试中会以大写形式出现。3.2 清洗规则表先看全再动手清洗不是简单的strip()和lower()。PDF 文本层经常混入全角空格、不间断空格、BOM 字符、以及音标字体映射失败产生的乱码。常见做法是按下面这张表逐项处理每处理完一步打印几条结果验证处理对象问题表现处理方式示例空白字符\u3000、\xa0混入统一替换为普通空格a\u3000b变a b大小写同词不同大小写统一小写入索引保留原始拼写Abandon索引为abandon音标乱码PDF 字体子集不完整保留原字符标记pending列待人工复核[əbændən]变[????]重复词条同一词出现在多页同词头合并释义用分号连接vt. 抛弃n. 抛弃合并派生词economic与economical易混不自动合并保留独立词条两者都保留合并重复词条时释义不要直接覆盖。同一单词在不同单元可能标注不同词性或义项把多个释义用分号连接反而更完整。from collections import defaultdict merged defaultdict(lambda: {phonetic: , meanings: []}) for rec in records: word rec[word].strip().lower() entry merged[word] if rec.get(phonetic): entry[phonetic] entry[phonetic] or rec[phonetic] if rec.get(meaning): entry[meanings].append(rec[meaning]) clean_records [ { word: w, phonetic: v[phonetic], meaning: .join(v[meanings]), } for w, v in merged.items() ]defaultdict的默认值是一个包含phonetic和meanings列表的字典。entry[phonetic] entry[phonetic] or rec[phonetic]表示只有当前词条还没有音标时才填充避免后出现的空字符串覆盖已有值。.join(v[meanings])用中文分号连接多释义Anki 正面和背面都能直接显示。3.3 把清洗结果导出成 TSV清洗后的数据结构化程度足够最适合导出为制表符分隔的 TSV 文件这是 Anki 和大多数本地工具都能消费的格式。import csv with open(words_clean.tsv, w, encodingutf-8, newline) as f: writer csv.writer(f, delimiter\t) writer.writerow([word, phonetic, meaning]) for rec in clean_records: writer.writerow([rec[word], rec[phonetic], rec[meaning]]) print(f清洗完成共 {len(clean_records)} 个词条已写入 words_clean.tsv)delimiter\t指定字段分隔符为制表符。newline防止 Windows 环境下 csv 模块写出多余空行。注意如果释义里本身包含 Tabs 或换行csv.writer会按规则加引号转义Anki 导入时也能正确识别。导出后直接对文件执行wc -l或head -5验证一下比在脚本里盲目自信靠谱得多。4. 把清洗后的大纲词汇批量生成 Anki 背词卡组4.1 字段设计与卡片模板Anki 的关键是把信息拆成最小字段而不是把整条释义塞进一个问题里。针对“北京成人本科学位英语考试大纲词汇总表.pdf”的复习场景建议三个字段word、phonetic、meaning。字段正面显示背面显示word是大号加粗否phonetic是正面的小字否meaning否是通过{{FrontSide}}带出再追加释义模板不用写得太复杂越简单加载越快。Anki 默认提供的 Basic 模型改三个字段名就能用或者直接用 genanki 在生成卡组时定义模型见 4.3。4.2 用 TSV 导入 Anki 的最短路径如果不想写额外代码直接把第 3 章导出的words_clean.tsv导入 Anki 也可以。操作顺序是Anki 中点“导入文件”文件类型选“文本分隔符文件”字段分隔符选“制表位”然后按顺序映射字段到word、phonetic、meaning。head -5 words_clean.tsv先看一眼文件前几行确认没有出现错列再进 Anki 导入。head -5在 Windows PowerShell 下可以写成Get-Content words_clean.tsv -TotalCount 5。这一步不需要写脚本适合一次性导入的场景。4.3 用 genanki 直接生成 .apkgTSV 导入适合快速起步但每次增删卡片都要重新导入麻烦。更省事的方式是用genanki库直接生成.apkg文件双击就能装进 Anki。import genanki model genanki.Model( 1607390001, 考纲词汇 Word/Meaning, fields[ {name: Word}, {name: Phonetic}, {name: Meaning}, ], templates[ { name: Card 1, qfmt: div stylefont-size:32px{{Word}}/div div stylefont-size:20px{{Phonetic}}/div, afmt: {{FrontSide}}hr idanswer{{Meaning}}, } ], ) deck genanki.Deck(2057390001, 北京成人本科学位英语考纲词汇) for rec in clean_records: if not rec[meaning]: continue note genanki.Note(modelmodel, fields[rec[word], rec[phonetic], rec[meaning]]) deck.add_note(note) genanki.Package(deck).write_to_file(beijing_degree_words.apkg)genanki.Model的第一个参数是模型 ID第二个是模型名。ID 可以填任意整数只要不和已有卡组冲突。qfmt和afmt是 Anki 卡片正反面的 HTML 模板{{Word}}会被替换为对应字段内容{{FrontSide}}表示把正面内容原样带到背面。genanki.Deck同理任意整数作 ID。if not rec[meaning]跳过空释义的记录避免生成一面只有单词没有任何答案的废卡。跑完后双击beijing_degree_words.apkg就能导入。4.4 复习参数怎么设才有参考意义卡组生成不是结束复习参数才是决定背不背得完的关键。考纲词汇量比四级少比高考多属于“集中突击型”词汇参数建议如下Anki 参数建议值理由新卡/天20~30总量有限每天 30 新卡两个月内能过完整轮复习上限100~150防止积压碎片时间能刷完初始间隔1 天第一遍只要求认得出简单间隔4 天与初始间隔拉开差距避免无意义重复成熟间隔21 天覆盖完整记忆周期搁置新卡10 分钟同一卡片单日尽量少见面这些参数在新版本 Anki 中都能在“牌组选项”里直接改。重点不是抄数值而是理解原则考纲词表有明确边界适合“全部过一遍再复刷”所以初始间隔可以激进一点成熟间隔拉长不让已经记住的词占满每日复习配额。5. 用 30 行脚本把考纲词表变成本地掌握率看板5.1 已掌握词对比脚本背了一段时间后最想知道的不是“今天复习了几张卡”而是“大纲词汇我到底掌握了多少”。Anki 自带统计能看到卡片数量但看不到考纲覆盖度。把已掌握的单词手动导出到一个learned_words.txt一行一个词再写个脚本和words_clean.tsv对比即可。from pathlib import Path master [] with open(words_clean.tsv, encodingutf-8) as f: for line in f: word line.split(\t)[0].strip().lower() if word: master.append(word) learned { line.strip().lower() for line in Path(learned_words.txt).read_text(encodingutf-8).splitlines() if line.strip() } unique list(dict.fromkeys(master)) unlearned [w for w in unique if w not in learned] rate (len(unique) - len(unlearned)) / len(unique) print(f大纲词表总量{len(unique)}) print(f已标记掌握{len(unique) - len(unlearned)}) print(f掌握率{rate:.1%}) print(f未掌握词条数{len(unlearned)}) print(按首字母分布)dict.fromkeys(master)在保留顺序的同时去重比 set 更稳定。learned是从已掌握文件读出的集合用于 O(1) 查询。line.split(\t)[0]只取每行第一个字段也就是单词避免把释义误当成单词统计。要快速生成learned_words.txt写一个简单脚本读 Anki 的导出文件即可不依赖任何插件。5.2 分组统计薄弱区在 5.1 的脚本末尾追加一个按首字母分组的统计能看到哪个字母区间还没过完。对成人考生来说这比“今天复习了 50 张卡”更有参考价值因为考试没有词汇大纲之外的超纲词覆盖度是硬指标。from collections import Counter unlearned_letters Counter(w[0].upper() for w in unlearned if w) for letter in sorted(unlearned_letters): print(f{letter}: {unlearned_letters[letter]:3d})输出到终端不够直观可以加一个参数重定向到文件python check_progress.py study_report.txtWindows 的cmd和 PowerShell 都支持这个写法。报告文件积累起来后直接比较前后两次的掌握率就能看到趋势。通常的方法是把这个脚本放在词表同级目录每次背完一组新词就更新learned_words.txt并重跑一遍。如果你希望自动化Windows 可以用任务计划程序macOS 用launchd把python check_progress.py设为每天固定时间执行一次输出追加到study.log。这样考纲词表的进度就不再依赖记忆力而是落在脚本输出的数字上。本文还有配套的精品资源点击获取
返回列表