news 2026/10/29 16:07:24

用Python实现词达人半自动答题:Selenium抓题与人工确认全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Python实现词达人半自动答题:Selenium抓题与人工确认全流程

简介:一套用Python实现的词达人半自动答题工具源码,既适合对文字游戏感兴趣、想借助脚本提升答题效率的玩家,也可作为Python初学者阅读项目结构和拆分功能的练习素材。压缩包内共有十个文件,类型上以七个功能脚本为主体,辅以一个JSON词库文件、一个依赖清单和一份说明文档,整个包体积仅4.43MB,轻便易部署。代码围绕数据获取、语义解析和答案推理三个层次组织:词库获取脚本负责更新本地数据,长句与单词处理脚本完成中英文解析,完整字典、中文短语和相关单词脚本提供校验与联想,补全句子脚本实现填空推理。阅读这份源码,可以直观理解从数据到交互的完整链路,包括如何处理上下文、设计函数接口、用正则和字典做文本匹配,也能在此基础上替换词库或调整推理逻辑,定制自己的答题助手。目前已有749人学习下载,是一个结构规整、参考价值较高的示例。

1. 词达人半自动答题,不追求全自动的原因很简单:不想被异常检测误伤

每天打开词达人,面对一组组长得像双胞胎的英文选项,连错几题就得从头计分,这种挫败感让不少人动了“干脆写个脚本”的念头。网上一搜,Python 写词达人半自动答题工具的源码包并不少见,名字里的“半自动”三个字,恰好点破了这类工具的定位:它不是全自动提交器,而是把题目从页面上抓下来、把候选答案算明白,最后那一下点击仍然留给人。这样既省掉反复翻词典、对照释义的枯燥,又把操作节奏控制在一个真实用户该有的频率上,大大降低被平台误判的风险。这篇笔记不搬运现成源码,只讲这类工具背后通用的实现思路:题目从哪里来、答案怎么匹配、人工确认环节怎么接,以及最容易翻车的几个细节。适合有一定 Python 基础、想自己写一个学习辅助脚本的读者。

2. 拆解题面链路:接口重放、浏览器自动化与“半自动”的定位

2.1 一次完整答题,在请求层能看到什么

词达人这类词汇练习平台,常见入口是微信小程序,部分学校或机构会开放浏览器 H5 版本。不管哪个入口,一次答题的请求链路都高度相似:进入练习时先请求一组题目,拿到题面后逐题作答,最后把结果批量提交。用抓包工具或者浏览器 DevTools 的 Network 面板盯一次完整流程,你会看到三类主要请求:练习初始化、题目内容、提交答案。

这里有一个很关键的区别:“题目内容”这个请求里,不同版本返回的数据结构差异很大。有的版本把题干、四个选项、甚至正确答案 ID 都直接放在 JSON 里,前端只是做展示和交互;有的版本则把选项顺序打乱后只给题面,正确答案根本不出现在响应里,需要后端在拿到你的答案之后再做判定。我第一次对接时就被“以为找到了答案字段”骗过:看到响应里有一个answer_id,兴冲冲做了全自动重放,结果那个字段只是下一道题的预览数据,白白浪费了一个晚上。

所以,我接手任何一份“词达人自动答题 Python 源码”时,第一步永远是抓包确认:正确答案到底在哪个环节出现,还是压根不出现。这直接决定后续方案选型。如果题面接口清晰、答案字段存在,用 requests 重放接口最省事;如果答案完全不可见,就得退回浏览器自动化,从渲染后的 DOM 里取题面,再用本地词库算候选答案。这也解释了为什么“半自动”比“全自动”更有韧性:就算拿不到正确选项,只要题面本身清晰,程序就能把题目拉回本地,算一个推荐答案让人来确认。页面交互那一环被降级为“只读加人工点击”,整体可维护性立刻上了一个台阶。

2.2 三条路线对比:requests 重放、Selenium 接管与 OCR 外挂

把实现方案拆开看,落地路径无非三条,各有各的适用场景。

技术路线接入成本抗页面改版能力被风控识别的风险典型场景
requests 接口重放低高,只要 token 逻辑不变中高,请求特征容易被统计接口里带答案 ID 的版本
Selenium/Playwright 浏览器接管中低中,需要同步更新选择器中,浏览器特征仍可疑页面结构稳定、需要人眼看界面的半自动场景
OCR 截图识别高高,不依赖 DOM中,鼠标键盘轨迹也得模拟页面是 Canvas 渲染或 DOM 全加密时

接口重放是很多“免费 Python 源码大全”里最爱用的方案,代码短、跑得快,但也是最容易翻车的:只要平台把返回体里的答案字段去掉,或者把接口加上签名参数,整套代码立刻报废。Selenium 路线相对稳,因为它模拟的是真实浏览器行为,页面怎么改,你最多改几个选择器;而且半自动场景下本来就需要一个真实浏览器窗口展示给用户看,Selenium 顺理成章成为我这套方案的主干。

OCR 路线看着很“黑科技”,实际性价比最低。你得先截屏、再识别单词、再做选项对齐,每一步都有误差叠加。除非有一天词达人把页面改成 Canvas 全绘制、DOM 里什么都不留,否则我不会优先选 OCR。真遇到那种情况,更合理的做法是直接放弃浏览器自动化,退回抓包看接口数据,别跟前端渲染死磕。

2.3 “半自动”的边界:推荐答案与自动点击本是两件事

很多初学者会把“半自动”理解成“代码没写完的自动答题”,其实不是。半自动是一个主动的设计决策,核心是把“计算答案”和“执行点击”拆成两件事。

计算答案这件事,程序有天然优势:本地维护一个词库,看到题干单词就能立刻映射出释义,再跟选项做匹配,准确率能做到比人肉翻词典高,速度还快得多。但“执行点击”这件事,程序并不总是比人做得好。点击太快、提交节奏过于均匀、脚本连续几十题零失误,这些特征在服务端看来都是强信号。尤其词达人这类平台本身就有过程监控,一旦判定你非正常作答,轻则清空成绩重做,重则限制账号练习资格。

把点击留给人之后,系统的行为模式就成了:程序给出推荐答案,人看一眼决策,然后动手点。节奏由人控制,准确率由程序兜底。这个模式既保留了效率提升,又让最终行为更像真人。实际开发里,我还会在推荐答案旁边显示一个“置信度分数”,低于某个阈值就明确提示用户别盲从,这能让半自动工具从“省事”进化成“辅助判断”,使用体验完全不同。

3. 用 Python 搭出半自动答题最小闭环:抓题、匹配、确认三步

3.1 环境准备:依赖安装、浏览器驱动与登录态复用

开始写代码之前,先把运行环境理清楚。这套方案基于 Python 3.8 以上版本,建议新建虚拟环境,避免把依赖装乱。就用标题里这句“Python 写了一个词达人半自动答题工具”作为项目目录名,把源码和词库分开存放,后面维护会轻松很多。

# 创建虚拟环境并激活 python -m venv .venv source .venv/bin/activate # Windows 下执行 .venv\Scripts\activate # 安装核心依赖 pip install selenium requests beautifulsoup4 lxml

依赖里最需要注意的是 Selenium。我用的是 Selenium 4,它自带 driver 管理能力,会在首次运行时自动去找匹配的 ChromeDriver。如果你的网络环境不允许自动下载,就去 Chrome 官网看当前浏览器版本,手动下载对应的 driver 放到 PATH 里,这是最常见的坑,后文避坑章节还会展开。

登录态复用是这类工具能否长期跑起来的关键。词达人一般需要扫码或账号密码登录,每次启动脚本都重新登录会很烦。常见做法是把 Chrome 的用户数据目录单独指定,让浏览器把登录后的 Cookie 落盘。

# driver.py from selenium import webdriver from selenium.webdriver.chrome.options import Options def create_driver(): opts = Options() # 关键参数:指定独立的用户数据目录,登录一次后续免登录 opts.add_argument("--user-data-dir=./chrome_profile") # 窗口尺寸固定,方便后续抓取页面元素定位 opts.add_argument("--window-size=1440,900") # 关闭“Chrome 正在受到自动软件控制”的提示条,减少页面元素偏移 opts.add_argument("--disable-blink-features=AutomationControlled") return webdriver.Chrome(options=opts)

这里最值得关注的是--user-data-dir参数。很多现成源码直接把默认的 Chrome 配置拿来用,每次启动都是全新会话,登录状态不保留,脚本跑一次就要重扫码,非常浪费时间。指定独立目录之后,第一次拿它打开词达人页面、手动完成登录,后续再启动就是已登录状态。--disable-blink-features=AutomationControlled是去掉页面里的自动化标记,虽然不能完全伪装成真人,但能少触发一些前端 JS 的检测逻辑。

3.2 轮询并解析当前题目:从页面 DOM 到结构化字典

环境就绪后,核心工作是从页面里把题目抓出来。词达人的题目类型不止一种:有英译中、中译英、选词填空、听音辨义等。不同题型的 DOM 结构差异很大,但都可以统一抽成同一个数据结构:题干文本加选项列表。

# fetcher.py from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time def fetch_current_question(driver): wait = WebDriverWait(driver, 10) # 等待题目容器出现;不同版本 class 名不同,以实际页面为准 card = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, ".question-card")) ) # 题干可能包含音频按钮、例句、题目正文,先取主文本 try: question_text = card.find_element( By.CSS_SELECTOR, ".question-title" ).text.strip() except Exception: question_text = "" # 选项通常是一组 li 或 div.option,循环提取文本 options = [] option_nodes = card.find_elements( By.CSS_SELECTOR, ".option-item" ) for node in option_nodes: # 去掉选项前的 A/B/C/D 序号,只保留内容 raw = node.text.strip() if raw and raw[0] in "ABCD" and raw[1] in ".、": raw = raw[2:].strip() options.append(raw) return {"question": question_text, "options": options}

这段代码有两个关键设计。第一,用presence_of_element_located做显式等待,而不是直接find_element,避免页面还在加载时抓到空节点。第二,选项文本做了一次前缀清理,去掉“A.”“B、”这类序号,因为后面做答案匹配时,需要纯粹的内容文本跟词库比对,带着序号会干扰相似度计算。

实际对接时会发现,不同版本的 class 命名经常变化,question-card、option-item这两个选择器不是通用的。我的习惯是先把页面 HTML 复制下来,在 DevTools 里用 CSS Selector 重新定位,再把选择器更新到上面的常量区里。这也是半自动方案的优势:哪怕选择器变了,你只需要改这里,不需要动整个点击逻辑。

3.3 候选答案生成:本地词库优先、在线释义兜底

题目抓下来之后,真正体现工具价值的是答案匹配模块。词达人练习的核心是单词释义对应关系,所以本地词库可以维护成一个 JSON 文件,键是英文单词,值是中文释义和相关义项。

# matcher.py import json import difflib def load_lexicon(path="lexicon.json"): with open(path, "r", encoding="utf-8") as f: return json.load(f) def pick_candidate(question_text, options, lexicon): """ 从题干里提取第一个英文单词,到词库找中文释义, 再把释义和所有选项做包含匹配加相似度打分。 """ # 题干可能是一整句话,先尝试提取核心单词 word = None for token in question_text.split(): # 过滤掉中文、标点和常见介词 clean = token.strip(",.?!;:'\"") if clean and clean.isalpha() and clean not in {"the", "a", "an", "of", "to", "in", "on"}: word = clean.lower() break entry = lexicon.get(word) if not entry: return None, 0.0 meaning = entry.get("meaning", "") best_option, best_score = None, 0.0 for opt in options: score = 0.0 # 释义直接出现在选项里,给一个大权重 if meaning and meaning in opt: score += len(meaning) * 2 # 用 difflib 做模糊匹配,处理词序变化的情况 score += difflib.SequenceMatcher(None, meaning, opt).ratio() if score > best_score: best_option, best_score = opt, score return best_option, best_score

这个匹配逻辑并不复杂,但有几个微妙的取舍。第一,题干抽词时过滤了常见介词和虚词,否则例句题会抓错关键词,拿到一个毫无意义的泛指词。第二,包含匹配的权重给得很高:如果词库里释义“抛弃”直接出现在某个选项里,得分会明显高于模糊匹配,这符合词达人英译中题型的实际出题规律。第三,difflib只作为兜底,处理释义和选项在措辞上有细微差异的情况,不能完全依赖它,中文短文本的相似度计算本身不太准。

本地词库覆盖不到的生词怎么办?常见做法是接一个在线词典接口做兜底。你可以用 requests 请求有道或其他你能访问的词典接口,把返回的释义塞回同一个匹配函数。这个兜底在冷启动阶段特别有用,词库还没积累起来时,能靠在线接口保证基础可用性。

3.4 人工确认环节:用 Tkinter 把最终选择权留给用户

半自动和全自动的分水岭就在这一步。程序算完候选答案,不直接控制浏览器去点,而是弹出一个确认界面,把题目、选项、推荐项一起展示给用户,等人点了“确认”按钮之后,脚本才把目光移回浏览器,用代码执行那一下点击。

# confirm.py import tkinter as tk def confirm_with_user(question, options, recommended): result = {} root = tk.Tk() root.title("词达人半自动答题 - 人工确认") tk.Label(root, text=question, wraplength=480, justify="left").pack(pady=10) var = tk.StringVar(value=recommended) for i, opt in enumerate(options): tk.Radiobutton( root, text=f"{opt}", variable=var, value=opt ).pack(anchor="w") def on_confirm(): result["selected"] = var.get() result["confirmed"] = True root.destroy() def on_skip(): result["selected"] = None result["confirmed"] = False root.destroy() tk.Button(root, text="确认并点击", command=on_confirm).pack(side="left", padx=20, pady=10) tk.Button(root, text="跳过这题", command=on_skip).pack(side="right", padx=20, pady=10) root.mainloop() return result.get("confirmed", False), result.get("selected")

确认框没有花哨的功能,核心要素就三个:题干展示、选项单选、两个按钮。推荐项通过StringVar默认选中,用户打开窗口就能看到程序建议的答案,正常情况下直接按回车确认即可,时间开销只在毫秒级。遇到程序没把握的题,用户可以在框里手动改选其他项。这个交互设计让“人机协同”落到实处——程序负责快,人负责准。

代码回到主流程后,拿到selected答案,用 Selenium 找到对应的选项元素执行.click(),再点击“下一题”。整个闭环就是:抓题、算答案、弹确认框、点击、下一题,循环往复。这也是标题里“半自动”三个字最直观的呈现。

4. 词达人答题脚本避坑记录:五个翻车现场与排查路径

4.1 现象:脚本跑了两天,突然读不到任何题目

脚本前几天跑得好好的,某天启动后控制台没有任何报错,但抓到的题干是空字符串,选项列表也是空的。打开浏览器窗口一看,页面停留在登录页或者一个空白练习入口,明显没有完成跳转。

原因基本可以锁定为登录态失效。词达人的会话过期机制一般不会在页面上明显提醒,服务端返回 401 后,前端 JS 静默做了跳转,但 Selenium 驱动的浏览器还没反应过来,fetch_current_question依然在旧页面里找元素,抓到的自然是空节点。

解决思路分两层。第一,在create_driver里启动浏览器后,先主动判断当前 URL 是不是登录页,是就停住等人工扫码,不是再进答题流程。第二,在fetch_current_question里检查题干文本的长度,如果连续三次抓到空字符串,就断开并触发重新登录流程,而不是无限重试。实操时我会再打印一层日志,把每次抓题的时间、题干长度、选项数量记录下来,排查时一眼就能看出是登录问题还是页面结构问题。

4.2 现象:题干抓出来了,选项却是错位的

题干文本显示的是第一题,选项列表里却是第三题的四个选项。更诡异的是,有时候选项是对的,但顺序跟页面上看到的不一致。这类问题通常在页面有加载动效或者懒加载机制时出现。

原因是页面里的题目容器和选项容器不是同时渲染的。题干先出来,选项要等字体资源、音频资源加载完成后才填充。脚本用presence_of_element_located等待的是容器本身存在,不代表容器内所有子元素都已就绪。常见做法是改成读取选项数量,确认数量达到了预期值再去解析内容;另外配合EC.visibility_of_element_located等待选项实际可见,而不是只等它在 DOM 里存在。

解决后还要加一道校验:解析出的选项数量如果少于两个或多于六个,视为异常,等待一秒钟重试。宁可多等零点几秒,也不要拿错位的数据去做答案匹配,否则确认框里展示的内容跟页面完全对不上,人工确认也就失去意义了。

4.3 现象:同义词干扰项被本地词库误判成正确答案

词达人出题时经常故意放一个同义词干扰项,词库里写着“abandon:抛弃”,选项里同时出现“抛弃”和“放弃”,程序看到“抛弃”就高亮锁定,但题目真正要选的是“放弃”。这就是典型的重合释义误判。

根本原因是词库结构过于简陋,只有一行中文释义,没有区分语义侧重点,也没有考虑题干上下文。特别是英译中题型里,近义词经常每个选项都长得像正确答案。

解决方法是给词库增加“同义词组”字段,把常用的近义词聚到一起。匹配时如果发现两个选项都在词条的同义词表里,就把推荐分数打平,并给置信度打一个特别低的分数,告诉用户这题别盲从。另一个做法是引入词性过滤:题干单词标了名词时,优先选名词性释义的选项,把动词性干扰项降权。这套逻辑会让你损失一部分自动推荐的准确率,但换来的是人工确认时的信任度——用户知道程序在拿不准时不会乱点鸳鸯谱。

4.4 现象:批量答题节奏太快,账号触发平台风控

有读者私信说过一个现象:脚本跑得很顺,十五分钟做了七十几题,第二天再登录发现练习进度清零,提示“存在非正常作答行为”。这个是最难排查的一类,因为页面看起来毫无异常,脚本也没有报错,但后台已经把你标记了。

原因很简单:答题速度远超正常人水平。词达人每题平均阅读加选择的时间在六到十秒,连续几十题零失误本身也反常。即使半自动方案保留了人工点击,如果用户本人就是个手速怪,连点十分钟不带停,照样会被盯上。

解决思路是在确认流程里加一个最小间隔约束。我一般会在点击“下一题”之前强制睡眠两到三秒,再随机浮动一点延迟,模拟真实阅读节奏。另一个更有效的做法是给答题数量设个安全阈值,比如连续答完三十题就弹窗提醒“休息一分钟”,让操作节奏自然拉长。半自动的价值在这里体现得最明显:你不需要写复杂的请求头伪装,只要把节奏控制好,行为模式就跟真人基本一致。

4.5 现象:页面一轮版后,旧选择器全部失效

某天词达人做了前端更新,页面视觉上几乎没变化,但脚本突然抓不到任何题目,DevTools 里一看,class 名称全变成了混淆后的短字符串,比如从.question-card变成了.q_3f2a。

这几乎是所有答题脚本的宿命。平台只要做一次前端重构,所有写死 CSS 选择器的代码就会集体报废。网上下载的很多源码,下载当天能用,三周之后大概率变成废纸,就是这个原因。

解决的常规思路是改用更稳定的定位策略。优先用文本内容定位,比如选项里的中文释义本身是稳定的,就算外层结构变了,文本不会变;或者用># recorder.py import json import datetime import os RECORD_FILE = "record.json" def append_record(question, options, recommended, user_choice, correct): record = { "time": datetime.datetime.now().isoformat(), "question": question, "options": options, "recommended": recommended, "user_choice": user_choice, "correct": correct } data = [] if os.path.exists(RECORD_FILE): with open(RECORD_FILE, "r", encoding="utf-8") as f: data = json.load(f) data.append(record) with open(RECORD_FILE, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2)

这个记录文件看着简单,实际价值非常大。你不需要在开跑前设计复杂的评估逻辑,只要把每次人工确认的结果原样记录下来,跑完 50 题后打开 JSON,按correct=false过滤,立刻能看到误判集中在哪种题型、那个单词上。

我一般会跑慢一点,每题结束后先把页面反馈对错抓回来,再记录一次correct字段。遇到误判时,顺手看一下recommended和user_choice的差异,基本就能定位问题出在词库缺释义还是匹配权重不对。这个习惯长期坚持下来,工具会越用越准。

5.2 把人工纠正的答案写回词库,实现增量更新

记录试跑不只是为了统计准确率,更核心的用途是把人工纠正的结果反哺给本地词库。人眼判断过的答案,尤其是那些程序推荐错、用户手动改对的题目,是修正词库最宝贵的数据。

我习惯在记录模块之外加一个同步函数,扫描当天的record.json,把所有correct=false且recommended != user_choice的记录提出来,自动对词库里的词条做补充。词库里没有的词自动新增,已有的词补一条同义词或近义词,旧的释义不直接删,而是降权处理,等下次试跑再验证。

这套增量更新机制让词库在连续使用两周之后,准确率会从最初的百分之七十左右爬到百分之九十五以上,因为常见干扰项都被手动纠偏过了。我自己的教训是:词库维护一定不要用一次性全量导入的方式,依赖冷启动时手动整理的词表,效果远不如一段时间的运行积累。半自动答题工具的真正价值不是一开始写的匹配逻辑有多聪明,而是它能让人在每道题上花一秒确认,却换来连续几个月的准确率增长。这套思路换个学习平台同样适用,甚至把你的六级高频词汇表导进去也能当背单词辅助工具用。

希望这篇笔记能帮你把词达人答题脚本做成一个真正属于自己的、越用越顺手的半自动工具。

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

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

YOLOv10实战:MNIST手写数字实时检测系统开发

1. 项目概述:当YOLOv10遇上数字识别去年在工业质检项目中第一次接触YOLO系列算法时,我就被这种"看一眼就定位"的检测方式震撼了。如今YOLOv10的发布,让实时检测的精度和效率又上了一个台阶。这次我们尝试用这个前沿框架&#xff0c…

作者头像 李华
网站建设 2026/10/29 16:05:17

系统性能调优参数详解与实战指南

1. 性能调优参数概述性能调优参数是系统优化过程中最关键的调节杠杆,它们像汽车发动机的精密仪表盘,每一个旋钮都直接影响着系统的运行效率。我在处理高并发交易系统时,曾通过调整JVM的GC参数将交易延迟从200ms降低到50ms,这种参数…

作者头像 李华
网站建设 2026/10/29 16:04:08

“Caveman”为何流行?从文化符号到极简生活与品牌设计

如果你最近刷社交平台、逛市集或者看品牌上新,一定有个感觉:“caveman”这个词出现的频率实在高得离谱。有人把原始人生存挑战当流量密码,有人在品牌命名里塞进一个山洞小人,还有人把“像原始人一样生活”当成一种对抗焦虑的解药。…

作者头像 李华
网站建设 2026/10/29 16:00:43

hyperframes:用HTML声明式编排MP4视频的Node.js CLI工具

1. 项目概述:什么是 hyperframes?它不是“视频帧”,而是 HTML 与 CLI 交界处的一次轻量级范式迁移你可能在 GitHub Trending 或 Node.js 社区的 CLI 工具推荐帖里见过hyperframes这个词——它既不像 FFmpeg 那样厚重,也不像 ffmpe…

作者头像 李华
网站建设 2026/10/29 16:00:38

中小企业业绩统计数字化转型:从Excel到智能分析

1. 项目概述:陀螺匠企业助手业绩统计模块在中小企业数字化管理实践中,业绩统计一直是运营管理的核心痛点。传统手工记录Excel表格的方式,不仅效率低下,还容易产生数据误差和版本混乱。陀螺匠企业助手正是瞄准这个细分场景&#xf…

作者头像 李华
网站建设 2026/10/29 15:56:51

Go REPL 引擎拆解:表达式求值器与行编辑库

1. 为什么交互式求值引擎要拆成两块拼图1.1 从“改一行跑一次”到“边敲边看”做过 Go 小工具的人大概都有这么一段经历:想验证一段表达式,比如某个位运算到底切成什么样、某个时间戳解析出来是不是预期值,于是打开编辑器写一个main.go&#…

作者头像 李华