告别Excel词库!用Python自动抓取四六级高频词生成智能背诵系统
还在为四六级考试发愁,每天抱着厚厚的单词书或者从网上下载的Excel词库,机械地重复着“abandon, abandon, 放弃...”吗?这种传统的背诵方式不仅效率低下,而且词库往往陈旧、缺乏针对性,无法适应快速变化的考试趋势。作为一名备考过英语等级考试的程序员,我深知这种痛苦。直到我开始用Python构建自己的智能背诵系统,才发现原来背单词可以如此高效和个性化。
想象一下,你的词库不再是静态的表格,而是一个能够自动从互联网上抓取最新高频词汇、分析词频、并根据你的记忆曲线动态调整学习计划的“活”系统。这听起来像是未来科技,但实际上,用Python的几个核心库就能轻松实现。今天,我就带你从零开始,打造一个属于你自己的、可扩展的智能背诵系统。我们将从网络爬虫获取动态词库切入,利用自然语言处理技术分析词汇重要性,最终集成到一个美观实用的桌面应用中。这套系统不仅能帮你应对四六级,其核心思路同样适用于考研、托福、GRE等任何需要大量词汇积累的场景。
1. 构建动态词库:从网络爬虫到高频词提取
传统的背单词程序,其词库往往是静态的文本或Excel文件。这意味着词汇是固定的,无法反映考试的最新趋势,也无法根据你的个人学习进度进行动态调整。我们的第一步,就是让词库“活”起来,通过Python爬虫技术,从权威的英语学习网站或考试真题库中,自动抓取并构建一个动态更新的高频词库。
1.1 选择合适的词源与爬虫策略
词源的选择至关重要。我们应优先考虑那些提供历年真题、高频词汇统计或模拟试题的网站。例如,一些大型的英语学习社区、考试官方网站的公开资料区,都是优质的词源。在开始爬取前,务必遵守网站的robots.txt协议,并采用礼貌的爬虫策略,如设置请求间隔、使用User-Agent等,避免对目标服务器造成负担。
这里,我们以模拟一个公开的英语学习论坛为例,假设其页面结构清晰,词汇列表以HTML表格或列表形式呈现。我们将使用requests库来发送HTTP请求,用BeautifulSoup库来解析HTML并提取我们需要的数据。
import requests from bs4 import BeautifulSoup import time import pandas as pd def fetch_vocabulary_from_web(url, headers=None): """ 从指定URL抓取词汇数据 """ if headers is None: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } try: response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() # 检查请求是否成功 response.encoding = 'utf-8' # 根据实际情况调整编码 return response.text except requests.RequestException as e: print(f"网络请求失败: {e}") return None def parse_vocabulary_html(html_content): """ 解析HTML内容,提取单词、音标、释义 假设目标页面中,单词信息在 class='word-item' 的div中 """ soup = BeautifulSoup(html_content, 'html.parser') word_list = [] # 根据实际网页结构调整选择器 word_items = soup.find_all('div', class_='word-item') for item in word_items: try: # 示例结构:<div class='word-item'><span class='spelling'>abandon</span> <span class='phonetic'>/əˈbændən/</span> <span class='definition'>vt. 放弃,遗弃</span></div> spelling = item.find('span', class_='spelling').get_text(strip=True) phonetic = item.find('span', class_='phonetic').get_text(strip=True) definition = item.find('span', class_='definition').get_text(strip=True) if spelling and definition: # 确保关键信息存在 word_list.append({ 'word': spelling, 'phonetic': phonetic, 'definition': definition }) except AttributeError: # 如果某个元素没找到,跳过该项 continue return word_list # 示例使用 if __name__ == '__main__': target_url = 'https://example-cet-vocabulary-site.com/level-4-high-frequency' # 替换为实际目标URL html = fetch_vocabulary_from_web(target_url) if html: words = parse_vocabulary_html(html) print(f"成功抓取到 {len(words)} 个单词") for w in words[:3]: # 预览前三个 print(w) # 间隔时间,避免请求过快 time.sleep(2)注意:在实际操作中,你需要使用真实的、允许爬取的网站URL,并仔细分析其HTML结构,调整
parse_vocabulary_html函数中的选择器。务必尊重网站版权和服务条款,仅用于个人学习目的。
1.2 多源整合与数据清洗
单一来源的词库可能不够全面。一个健壮的系统应该能够从多个来源抓取数据,并进行去重和整合。我们可以将上述抓取函数封装成一个类,管理多个词源URL。
class VocabularyCrawler: def __init__(self): self.sources = { 'cet4_高频': 'https://source1.com/cet4-high', 'cet6_真题': 'https://source2.com/cet6-past-papers', 'cet6_核心': 'https://source3.com/cet6-core' } self.all_words = [] def crawl_all_sources(self): for source_name, url in self.sources.items(): print(f"正在抓取来源: {source_name}") html = fetch_vocabulary_from_web(url) if html: words = parse_vocabulary_html(html) # 为每个单词添加来源标签 for w in words: w['source'] = source_name self.all_words.extend(words) print(f" 从 {source_name} 抓取到 {len(words)} 个单词") time.sleep(1) # 礼貌的爬虫间隔 def deduplicate_and_save(self, output_path='raw_vocabulary.csv'): """去重并保存为CSV文件""" if not self.all_words: print("没有抓取到数据") return df = pd.DataFrame(self.all_words) # 基于单词拼写去重,保留第一个出现的记录(或可以根据需要制定更复杂的规则) df_dedup = df.drop_duplicates(subset=['word'], keep='first') df_dedup.to_csv(output_path, index=False, encoding='utf-8-sig') print(f"去重后共有 {len(df_dedup)} 个唯一单词,已保存至 {output_path}") return df_dedup # 使用示例 crawler = VocabularyCrawler() crawler.crawl_all_sources() df_vocab = crawler.deduplicate_and_save()抓取到的原始数据往往存在格式不统一、释义冗余、包含无关字符等问题。因此,数据清洗是必不可少的一步。我们可以编写清洗函数来处理常见问题:
- 统一大小写:将所有单词转换为小写,便于后续处理。
- 清理释义:移除释义中的HTML标签、多余空格、无关符号。
- 标准化音标:确保音标格式一致。
- 分类标记:根据词性(如
vt.,n.,adj.)对释义进行初步拆分和标记。
通过多源抓取和精细清洗,我们获得了一个比任何静态词库都更丰富、更贴近考试动态的原始词库。但这还不够,我们需要知道哪些词才是真正的“高频”和“核心”。
2. 词频分析与智能筛选:让数据告诉你该背什么
拥有原始词库后,下一步是运用自然语言处理(NLP)技术,从海量词汇中筛选出最具学习价值的部分。盲目背诵所有单词是低效的,我们需要依据词频、考试真题出现频率、以及词汇本身的难易度和关联性,构建一个智能的词汇优先级体系。
2.1 基于NLTK的词频统计与词形还原
我们可以利用nltk库对历年四六级考试的真题文本(需事先收集或从公开渠道获取)进行词频分析。高频出现的词汇,自然是备考的重点。
import nltk from nltk.corpus import stopwords from nltk.stem import WordNetLemmatizer from collections import Counter import re # 确保已下载必要的nltk数据包 # nltk.download('punkt') # nltk.download('stopwords') # nltk.download('wordnet') def analyze_word_frequency(text_corpus): """ 分析文本语料库中的单词频率 text_corpus: 字符串,包含所有真题文本 """ # 1. 分词 tokens = nltk.word_tokenize(text_corpus.lower()) # 转为小写并分词 # 2. 去除标点符号和纯数字 words = [word for word in tokens if word.isalpha()] # 3. 去除停用词(如 the, a, an, in, on 等) stop_words = set(stopwords.words('english')) filtered_words = [word for word in words if word not in stop_words] # 4. 词形还原(将不同形式的单词归并为原形,如 running -> run) lemmatizer = WordNetLemmatizer() lemmatized_words = [lemmatizer.lemmatize(word) for word in filtered_words] # 5. 统计词频 word_freq = Counter(lemmatized_words) # 返回最常见的N个词及其频率 most_common = word_freq.most_common(1000) # 获取前1000个高频词 return dict(most_common) # 假设我们已经将多年真题文本合并到了一个字符串变量 `all_past_papers_text` 中 # high_freq_dict = analyze_word_frequency(all_past_papers_text)然而,仅凭真题词频还不够。我们还需要结合词汇的“考试价值”。例如,一些词汇虽然总体频率不高,但在完形填空、阅读理解的关键处频繁出现,或者是一词多义、搭配灵活的重点难点词。我们可以引入一个简单的评分模型:
词汇优先级分数 = 真题词频权重 + 网络热度权重 + 词库覆盖权重
我们可以设计一个Pandas DataFrame来管理这些元数据,并进行计算。
def calculate_word_priority(raw_vocab_df, high_freq_dict, online_hotness_dict=None): """ 计算每个单词的优先级分数 raw_vocab_df: 原始词库DataFrame high_freq_dict: 真题高频词字典 {word: frequency} online_hotness_dict: 网络热度字典(可从学习平台、搜索指数获取),{word: hotness_score} """ df = raw_vocab_df.copy() # 1. 真题词频得分 (归一化到0-1) df['past_paper_freq'] = df['word'].map(high_freq_dict).fillna(0) max_freq = df['past_paper_freq'].max() df['freq_score'] = df['past_paper_freq'] / max_freq if max_freq > 0 else 0 # 2. 网络热度得分 (如果有) if online_hotness_dict: df['online_hotness'] = df['word'].map(online_hotness_dict).fillna(0) max_hot = df['online_hotness'].max() df['hotness_score'] = df['online_hotness'] / max_hot if max_hot > 0 else 0 else: df['hotness_score'] = 0 # 3. 词库覆盖得分 (衡量该词在多个来源中出现的情况) # 假设raw_vocab_df有'source'列,记录来源 source_count = df.groupby('word')['source'].nunique().reset_index(name='source_count') df = df.merge(source_count, on='word', how='left') max_source = df['source_count'].max() df['coverage_score'] = df['source_count'] / max_source if max_source > 0 else 0 # 4. 综合优先级分数 (可调整权重) df['priority_score'] = ( 0.5 * df['freq_score'] + 0.3 * df['hotness_score'] + 0.2 * df['coverage_score'] ) # 按优先级降序排序 df_sorted = df.sort_values(by='priority_score', ascending=False).drop_duplicates(subset=['word']) return df_sorted[['word', 'phonetic', 'definition', 'priority_score', 'past_paper_freq', 'source_count']] # 假设我们已经有了 raw_vocab_df 和 high_freq_dict # prioritized_vocab = calculate_word_priority(raw_vocab_df, high_freq_dict) # print(prioritized_vocab.head(20))2.2 构建分级词库与学习计划
根据计算出的priority_score,我们可以将词汇分为不同的等级,例如:
- 核心高频词 (Priority > 0.8):必须熟练掌握,每天优先复习。
- 重点词汇 (0.5 < Priority <= 0.8):需要重点记忆,安排定期复习。
- 拓展词汇 (Priority <= 0.5):作为补充,在时间充裕时学习。
我们可以将分级后的词库保存下来,并为其生成一个初步的学习计划表。这个计划表可以是一个CSV文件,包含单词、计划学习日期、复习间隔(基于艾宾浩斯记忆曲线)等字段。
import datetime from dateutil.relativedelta import relativedelta def generate_study_plan(prioritized_df, start_date=None, daily_new_words=20): """ 根据优先级生成学习计划表 prioritized_df: 按优先级排序的词库DataFrame start_date: 计划开始日期,默认为明天 daily_new_words: 每天学习的新单词数量 """ if start_date is None: start_date = datetime.date.today() + datetime.timedelta(days=1) plan = [] word_list = prioritized_df.to_dict('records') total_words = len(word_list) batches = [word_list[i:i + daily_new_words] for i in range(0, total_words, daily_new_words)] # 简单的艾宾浩斯复习间隔:1天后,7天后,16天后,35天后 review_intervals = [1, 7, 16, 35] for batch_idx, batch in enumerate(batches): learn_date = start_date + datetime.timedelta(days=batch_idx) for word_item in batch: word_plan = { 'word': word_item['word'], 'phonetic': word_item['phonetic'], 'definition': word_item['definition'], 'priority': word_item['priority_score'], 'first_learn_date': learn_date.isoformat(), } # 计算复习日期 for i, interval in enumerate(review_intervals): review_date = learn_date + datetime.timedelta(days=interval) word_plan[f'review_date_{i+1}'] = review_date.isoformat() plan.append(word_plan) plan_df = pd.DataFrame(plan) return plan_df # 生成计划 # study_plan_df = generate_study_plan(prioritized_vocab.head(500), daily_new_words=25) # study_plan_df.to_csv('cet6_study_plan.csv', index=False, encoding='utf-8-sig')至此,我们已经从一个被动的词库使用者,转变为一个主动的词库构建者和分析者。我们拥有的不再是一堆冰冷的单词列表,而是一个带有智能评分、分级和个性化学习计划的数据驱动系统。接下来,我们要为这个强大的“大脑”配上一个好用的“身体”——图形用户界面。
3. 打造智能交互界面:Tkinter与现代化GUI设计
一个优秀的背诵程序,其界面不仅要功能齐全,更要符合用户的使用习惯,提升学习体验。我们将使用Python内置的Tkinter库来构建桌面应用。虽然Tkinter有时被认为其貌不扬,但通过合理的布局和现代风格的配置,完全可以打造出简洁、高效的界面。
3.1 应用架构与主界面设计
我们的应用核心功能模块包括:词库管理、每日学习、复习测试、数据统计。主界面应该清晰地将这些功能呈现给用户。
import tkinter as tk from tkinter import ttk, messagebox, scrolledtext import pandas as pd from datetime import date import random class SmartVocabApp: def __init__(self, root): self.root = root self.root.title("智能四六级词汇背诵系统") self.root.geometry("1000x700") # 加载词库和学习计划 self.vocab_df = None self.study_plan_df = None self.today_words = [] # 今日待学习/复习的单词列表 self.current_word_index = 0 self.known_words = set() # 已掌握的单词 self.hard_words = set() # 难词 self.setup_styles() self.create_widgets() self.load_data() def setup_styles(self): """配置界面样式""" style = ttk.Style() style.theme_use('clam') # 使用一个相对现代的theme # 配置按钮、标签等样式 style.configure('Title.TLabel', font=('微软雅黑', 16, 'bold')) style.configure('Header.TLabel', font=('微软雅黑', 12)) style.configure('Word.TLabel', font=('Consolas', 24, 'bold'), foreground='#2c3e50') style.configure('Phonetic.TLabel', font=('Arial', 14), foreground='#7f8c8d') style.configure('Definition.TLabel', font=('微软雅黑', 14), foreground='#34495e') style.configure('Action.TButton', font=('微软雅黑', 11), padding=10) def create_widgets(self): """创建主界面所有组件""" # 顶部状态栏 status_frame = ttk.Frame(self.root, padding="10") status_frame.grid(row=0, column=0, columnspan=3, sticky=(tk.W, tk.E)) ttk.Label(status_frame, text="智能词汇系统", style='Title.TLabel').pack(side=tk.LEFT) self.date_label = ttk.Label(status_frame, text=f"日期: {date.today()}", style='Header.TLabel') self.date_label.pack(side=tk.RIGHT, padx=20) self.progress_label = ttk.Label(status_frame, text="进度: 0/0", style='Header.TLabel') self.progress_label.pack(side=tk.RIGHT, padx=20) # 左侧控制面板 control_frame = ttk.LabelFrame(self.root, text="学习控制", padding="15") control_frame.grid(row=1, column=0, sticky=(tk.N, tk.S, tk.W, tk.E), padx=10, pady=10) ttk.Button(control_frame, text="开始今日学习", command=self.start_daily_study, style='Action.TButton').pack(fill=tk.X, pady=5) ttk.Button(control_frame, text="复习难词", command=self.review_hard_words, style='Action.TButton').pack(fill=tk.X, pady=5) ttk.Button(control_frame, text="随机测试", command=self.random_quiz, style='Action.TButton').pack(fill=tk.X, pady=5) ttk.Button(control_frame, text="学习统计", command=self.show_stats, style='Action.TButton').pack(fill=tk.X, pady=5) ttk.Button(control_frame, text="更新词库", command=self.update_vocabulary, style='Action.TButton').pack(fill=tk.X, pady=5) # 中间主学习区 learn_frame = ttk.LabelFrame(self.root, text="单词学习", padding="20") learn_frame.grid(row=1, column=1, sticky=(tk.N, tk.S, tk.W, tk.E), padx=10, pady=10, rowspan=2) self.root.columnconfigure(1, weight=1) self.root.rowconfigure(1, weight=1) # 单词显示区域 self.word_label = ttk.Label(learn_frame, text="", style='Word.TLabel') self.word_label.pack(pady=(20, 5)) self.phonetic_label = ttk.Label(learn_frame, text="", style='Phonetic.TLabel') self.phonetic_label.pack(pady=5) # 释义区域 - 初始隐藏,点击后显示 self.definition_frame = ttk.Frame(learn_frame) self.definition_frame.pack(pady=20, fill=tk.BOTH, expand=True) ttk.Label(self.definition_frame, text="释义:", style='Header.TLabel').pack(anchor=tk.W) self.definition_text = scrolledtext.ScrolledText(self.definition_frame, height=6, width=50, font=('微软雅黑', 12), wrap=tk.WORD) self.definition_text.pack(fill=tk.BOTH, expand=True) self.definition_text.config(state=tk.DISABLED) # 初始为只读 # 操作按钮区域 button_frame = ttk.Frame(learn_frame) button_frame.pack(pady=30) ttk.Button(button_frame, text="显示释义", command=self.show_definition, style='Action.TButton').grid(row=0, column=0, padx=5) ttk.Button(button_frame, text="太简单 (已掌握)", command=self.mark_as_known, style='Action.TButton').grid(row=0, column=1, padx=5) ttk.Button(button_frame, text="加入难词本", command=self.mark_as_hard, style='Action.TButton').grid(row=0, column=2, padx=5) ttk.Button(button_frame, text="下一个", command=self.next_word, style='Action.TButton').grid(row=0, column=3, padx=5) # 右侧信息面板 info_frame = ttk.LabelFrame(self.root, text="学习信息", padding="15") info_frame.grid(row=1, column=2, sticky=(tk.N, tk.S, tk.W, tk.E), padx=10, pady=10) self.stats_text = scrolledtext.ScrolledText(info_frame, height=15, width=30, font=('微软雅黑', 10)) self.stats_text.pack(fill=tk.BOTH, expand=True) self.stats_text.config(state=tk.DISABLED) # 底部日志区域 log_frame = ttk.LabelFrame(self.root, text="学习日志", padding="10") log_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E), padx=10, pady=(0, 10)) self.root.columnconfigure(0, weight=1) self.log_text = scrolledtext.ScrolledText(log_frame, height=8, font=('Consolas', 9)) self.log_text.pack(fill=tk.BOTH, expand=True) self.log_text.config(state=tk.DISABLED) def load_data(self): """加载词库和学习计划数据""" try: # 这里应替换为实际的数据加载逻辑,例如从CSV或数据库读取 # self.vocab_df = pd.read_csv('prioritized_vocabulary.csv') # self.study_plan_df = pd.read_csv('study_plan.csv') self.log("系统初始化完成,数据已加载。") except FileNotFoundError: messagebox.showwarning("数据缺失", "未找到词库文件,请先点击'更新词库'。") self.log("警告:词库文件未找到。")这个主界面框架清晰地划分了功能区、学习区、信息区和日志区。接下来,我们需要实现最核心的学习逻辑。
3.2 实现核心学习逻辑与交互
学习过程的核心是状态管理:显示单词、记录用户反馈、更新进度、并决定下一个要学习的单词。我们需要完善SmartVocabApp类中的几个关键方法。
def start_daily_study(self): """开始今日学习任务""" if self.study_plan_df is None: messagebox.showerror("错误", "请先加载或生成学习计划。") return today_str = date.today().isoformat() # 从学习计划中找出今天首次学习或需要复习的单词 # 简化逻辑:找出 first_learn_date 或任意 review_date 为今天的单词 today_learn = self.study_plan_df[ (self.study_plan_df['first_learn_date'] == today_str) | (self.study_plan_df['review_date_1'] == today_str) | (self.study_plan_df['review_date_2'] == today_str) | (self.study_plan_df['review_date_3'] == today_str) ] if today_learn.empty: messagebox.showinfo("提示", "恭喜!今天没有新的学习或复习任务。") self.log("今日无任务。") return # 过滤掉已掌握的单词 today_words_filtered = [row for _, row in today_learn.iterrows() if row['word'] not in self.known_words] self.today_words = today_words_filtered self.current_word_index = 0 if not self.today_words: messagebox.showinfo("提示", "今日所有任务单词均已掌握!") self.log("今日任务单词已全部掌握。") return random.shuffle(self.today_words) # 打乱顺序 self.display_current_word() self.update_progress() self.log(f"开始今日学习,共 {len(self.today_words)} 个单词。") def display_current_word(self): """显示当前单词(隐藏释义)""" if self.current_word_index < len(self.today_words): word_info = self.today_words[self.current_word_index] self.word_label.config(text=word_info['word']) self.phonetic_label.config(text=word_info.get('phonetic', '/ /')) # 清空并禁用释义文本框 self.definition_text.config(state=tk.NORMAL) self.definition_text.delete(1.0, tk.END) self.definition_text.insert(tk.END, word_info['definition']) self.definition_text.config(state=tk.DISABLED) # 隐藏释义区域内容,只显示空白或提示 self.definition_text.delete(1.0, tk.END) self.definition_text.insert(tk.END, "[点击‘显示释义’查看]") self.definition_text.config(state=tk.DISABLED) else: self.word_label.config(text="今日任务完成!") self.phonetic_label.config(text="") self.definition_text.config(state=tk.NORMAL) self.definition_text.delete(1.0, tk.END) self.definition_text.insert(tk.END, "恭喜你完成了今天的学习任务。") self.definition_text.config(state=tk.DISABLED) def show_definition(self): """显示当前单词的完整释义""" self.definition_text.config(state=tk.NORMAL) self.definition_text.delete(1.0, tk.END) if self.current_word_index < len(self.today_words): word_info = self.today_words[self.current_word_index] self.definition_text.insert(tk.END, word_info['definition']) self.definition_text.config(state=tk.DISABLED) def mark_as_known(self): """标记为已掌握""" if self.current_word_index < len(self.today_words): word = self.today_words[self.current_word_index]['word'] self.known_words.add(word) self.log(f"已将 '{word}' 标记为已掌握。") self.next_word() def mark_as_hard(self): """标记为难词""" if self.current_word_index < len(self.today_words): word = self.today_words[self.current_word_index]['word'] self.hard_words.add(word) self.log(f"已将 '{word}' 加入难词本。") # 可以在这里将难词保存到文件 self.next_word() def next_word(self): """切换到下一个单词""" self.current_word_index += 1 self.update_progress() if self.current_word_index < len(self.today_words): self.display_current_word() else: self.display_current_word() # 显示完成信息 messagebox.showinfo("完成", "今日学习任务已完成!") # 可选:自动保存学习记录 self.save_learning_progress() def update_progress(self): """更新进度标签""" total = len(self.today_words) current = min(self.current_word_index + 1, total) if total > 0 else 0 self.progress_label.config(text=f"进度: {current}/{total}") def log(self, message): """向日志区域添加一条消息""" self.log_text.config(state=tk.NORMAL) timestamp = datetime.datetime.now().strftime("%H:%M:%S") self.log_text.insert(tk.END, f"[{timestamp}] {message}\n") self.log_text.see(tk.END) # 滚动到底部 self.log_text.config(state=tk.DISABLED) def save_learning_progress(self): """保存学习进度(已知单词、难词等)到文件""" # 这里实现将 known_words 和 hard_words 保存到JSON或CSV的逻辑 self.log("学习进度已自动保存。")通过上述代码,我们实现了一个完整的、交互式的学习流程。用户可以看到单词和音标,思考后点击“显示释义”核对,然后根据掌握情况选择“已掌握”或“加入难词本”。系统会记录这些反馈,用于优化未来的学习计划。
4. 系统优化与扩展:让学习更高效、更智能
一个基础可用的系统已经搭建完成,但要让它真正变得“智能”和“高效”,我们还需要从数据持久化、复习算法、个性化推荐以及部署便捷性等方面进行深度优化。
4.1 数据持久化与状态管理
目前,用户的学习状态(如已掌握单词、难词)仅保存在内存中,程序关闭后就会丢失。我们需要将其持久化到本地文件或轻量级数据库中。这里我们选择使用SQLite,它无需单独安装,非常适合桌面应用。
import sqlite3 import json from pathlib import Path class LearningDatabase: def __init__(self, db_path='learning_data.db'): self.db_path = Path(db_path) self.init_database() def init_database(self): """初始化数据库表""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() # 用户学习记录表 cursor.execute(''' CREATE TABLE IF NOT EXISTS user_progress ( word TEXT PRIMARY KEY, first_seen_date TEXT, last_review_date TEXT, review_count INTEGER DEFAULT 0, ease_factor REAL DEFAULT 2.5, -- 用于间隔重复算法的熟练度因子 next_review_date TEXT, is_known INTEGER DEFAULT 0, -- 0:未掌握, 1:已掌握 is_hard INTEGER DEFAULT 0 -- 0:非难词, 1:难词 ) ''') # 学习日志表 cursor.execute(''' CREATE TABLE IF NOT EXISTS study_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT, word TEXT, action TEXT, -- 'learn', 'review', 'marked_known', 'marked_hard' response_time REAL -- 可选:记录用户反应时间 ) ''') conn.commit() conn.close() def update_word_progress(self, word, action, response_time=None): """更新单词的学习进度""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() today = date.today().isoformat() cursor.execute('SELECT * FROM user_progress WHERE word = ?', (word,)) record = cursor.fetchone() if record is None: # 新单词 cursor.execute(''' INSERT INTO user_progress (word, first_seen_date, last_review_date, review_count, next_review_date) VALUES (?, ?, ?, 1, ?) ''', (word, today, today, self._calculate_next_review(today, 2.5, 1))) else: # 更新现有记录 review_count = record[3] + 1 ease_factor = record[4] if action == 'marked_known': # 答对了,根据SM-2算法更新间隔和熟练度因子 ease_factor = max(1.3, ease_factor + 0.1 - (5 - 5) * (0.08 + (5 - 5) * 0.02)) # 简化版SM-2 is_known = 1 elif action == 'marked_hard': ease_factor = max(1.3, ease_factor - 0.2) is_hard = 1 else: is_known = record[6] is_hard = record[7] next_review = self._calculate_next_review(today, ease_factor, review_count) cursor.execute(''' UPDATE user_progress SET last_review_date=?, review_count=?, ease_factor=?, next_review_date=?, is_known=?, is_hard=? WHERE word=? ''', (today, review_count, ease_factor, next_review, is_known, is_hard, word)) # 记录学习日志 cursor.execute(''' INSERT INTO study_logs (timestamp, word, action, response_time) VALUES (?, ?, ?, ?) ''', (datetime.datetime.now().isoformat(), word, action, response_time)) conn.commit() conn.close() def _calculate_next_review(self, last_date, ease_factor, review_count): """基于SM-2间隔重复算法计算下次复习日期(简化版)""" from datetime import datetime, timedelta last = datetime.fromisoformat(last_date) if review_count == 1: interval = 1 elif review_count == 2: interval = 6 else: interval = int((review_count - 1) * ease_factor) next_date = last + timedelta(days=interval) return next_date.isoformat() def get_todays_review_words(self): """获取今天需要复习的单词""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() today = date.today().isoformat() cursor.execute(''' SELECT word FROM user_progress WHERE next_review_date <= ? AND is_known = 0 ORDER BY next_review_date ASC ''', (today,)) words = [row[0] for row in cursor.fetchall()] conn.close() return words将这个数据库类集成到主应用中,我们就可以实现学习状态的持久化和基于算法的智能复习调度。SM-2算法是知名记忆软件Anki使用的核心算法,它能根据你对每个单词的熟悉程度,动态调整下次复习的时间,确保在即将遗忘时进行复习,从而最大化记忆效率。
4.2 个性化推荐与自适应学习路径
系统可以根据用户的历史表现数据,动态调整学习内容和难度。
- 薄弱环节分析:通过分析错题本(难词本)和答题反应时间,识别用户常错的词性(如动词、名词)、特定前缀后缀、或某个语义场(如学术词汇、生活词汇)。然后从词库中推荐同类型或相关的词汇进行强化学习。
- 动态调整每日任务量:如果用户连续几天完成率很高、反应时间短,可以适当增加每日新学单词量;反之,如果正确率下降,则减少新词量,增加复习比重。
- 兴趣关联:如果系统集成了更丰富的词库(如科技、商务、文学等分类),可以根据用户标记“喜欢”或“已掌握”的单词类型,推荐相关领域的词汇,增加学习趣味性。
def generate_personalized_review(user_db, vocab_df, num_words=20): """ 生成个性化复习列表 策略:优先复习即将到期的单词,其次复习标记为‘难词’的单词,最后补充一些用户掌握较好但久未复习的单词(防止遗忘) """ conn = sqlite3.connect(user_db) # 1. 获取即将到期和难词 today = date.today().isoformat() query_urgent = ''' SELECT word FROM user_progress WHERE next_review_date <= ? AND is_known = 0 ORDER BY next_review_date ASC LIMIT ? ''' cursor = conn.cursor() cursor.execute(query_urgent, (today, num_words // 2)) urgent_words = [row[0] for row in cursor.fetchall()] # 2. 获取标记为难词的单词(随机选取) query_hard = ''' SELECT word FROM user_progress WHERE is_hard = 1 AND is_known = 0 AND word NOT IN ({}) ORDER BY RANDOM() LIMIT ? '''.format(','.join('?'*len(urgent_words))) if urgent_words else 'SELECT word FROM user_progress WHERE is_hard = 1 AND is_known = 0 ORDER BY RANDOM() LIMIT ?' params = urgent_words + [num_words // 4] if urgent_words else [num_words // 4] cursor.execute(query_hard, params) hard_words = [row[0] for row in cursor.fetchall()] # 3. 获取“熟悉的陌生人”:掌握但很久没复习的单词 query_old = ''' SELECT word FROM user_progress WHERE is_known = 1 AND julianday(?) - julianday(last_review_date) > 60 ORDER BY RANDOM() LIMIT ? ''' remaining = num_words - len(urgent_words) - len(hard_words) cursor.execute(query_old, (today, remaining)) old_words = [row[0] for row in cursor.fetchall()] conn.close() # 合并列表,并从总词库中获取完整信息 all_word_names = list(set(urgent_words + hard_words + old_words))[:num_words] personalized_list = vocab_df[vocab_df['word'].isin(all_word_names)].to_dict('records') random.shuffle(personalized_list) # 打乱顺序 return personalized_list4.3 部署与扩展:从脚本到可分发应用
为了让非技术背景的用户也能方便地使用,我们需要将Python脚本打包成独立的可执行文件(.exe或.app)。PyInstaller是一个完美的工具。
首先,确保项目结构清晰,例如:
SmartVocabApp/ ├── main.py # 主程序入口 ├── crawler.py # 爬虫和词频分析模块 ├── database.py # 数据库操作模块 ├── gui.py # GUI界面模块 ├── data/ │ ├── vocabulary.db # SQLite数据库 │ └── word_lists/ # 存放原始和处理的词库文件 └── resources/ # 图标、音效等资源文件然后,使用PyInstaller进行打包:
# 安装 PyInstaller pip install pyinstaller # 打包为单个可执行文件(包含所有依赖) pyinstaller --onefile --windowed --icon=resources/icon.ico --name "SmartVocabApp" main.py # 或者打包为一个目录(便于调试和查看日志) pyinstaller --windowed --icon=resources/icon.ico --name "SmartVocabApp" main.py打包后,你会得到一个dist文件夹,里面包含了可以直接分发给其他用户的可执行程序,他们无需安装Python或任何库即可运行。
此外,我们还可以考虑以下扩展方向:
- 云端同步:使用简单的Web API(如Flask + SQLite)实现学习进度在多设备间的同步。
- 语音合成:集成
pyttsx3或调用在线TTS API,为单词提供发音功能,强化听觉记忆。 - 移动端适配:利用
Kivy或BeeWare框架,将核心逻辑移植到手机App上,实现随时随地学习。
通过这一系列的优化和扩展,你的智能背诵系统就从一个小工具,进化成了一个功能全面、体验流畅、具备一定“智能”的个性化学习伴侣。它不再仅仅是一个显示单词的程序,而是一个能够理解你的学习状态、预测你的记忆规律、并主动为你规划最优学习路径的智能系统。