
简介这是一套基于Python开发的链工宝安全生产月答题自动化工具源码面向企业安全管理人员、安监培训学员及Python自动化实践者解决人工重复答题耗时、效率低、易出错等问题。资源包共12个文件含3个核心Python脚本Process_wx.py、main.py、Questions.py、2个JSON题库文件unkown_ques.json、answer.json、5张关键操作截图如auth_with_wetch.png、script_prop.png等及配置说明文档README.md、requirements.txt整体压缩后仅3.81MB轻量易部署。已有198人学习下载体现了基层安全培训场景中对自动化提效工具的实际需求。读者可直接复用完整请求流程逻辑获取微信授权后的Cookie与Token注入机制、定时答题调度实现schedule库、题库动态加载结构以及BurpSuite抓包适配技巧具备即装即用与二次开发双重价值。1. 这不是“外挂”而是一套可审计、可复现的安全生产知识自动化验证方案很多企业安全部门同事在每年6月“安全生产月”期间要组织全员完成链工宝平台的在线答题任务——不是一次而是每日一练专项考试最终考核动辄上千人、上百道题、多轮次提交。人工盯进度、催答题、导出错题、二次讲解耗时耗力还容易漏人。有人用录屏回放、有人用按键精灵但这些方式既不稳定页面结构微调即失效又缺乏过程留痕更无法对接内部培训系统。本方案用 Python 构建一套完全基于链工宝公开 Web 接口与 DOM 结构的自动化答题工具不注入脚本、不绕过登录校验、不模拟用户真实点击而是通过标准 HTTP 请求 精确 DOM 解析 题目语义匹配实现“答得准、留得清、改得快”。适合安全管理员、EHS 工程师、IT 培训支持岗——你不需要会写爬虫但需要能看懂请求头、能改 selector、能验证答案逻辑。2. 为什么选 Requests BeautifulSoup Selenium 组合而非纯无头浏览器或 JS 注入2.1 链工宝前端行为特征决定技术栈取舍链工宝答题页并非单页应用SPA全由 Vue/React 渲染其核心题库展示、选项渲染、提交响应均依赖服务端直出 HTML仅部分交互如倒计时、防重复提交使用轻量级 JS。这意味着纯 Requests 可获取题目主干GET/exam/start?examIdxxx返回完整 HTML含题干、选项、题型标识单选/多选/判断、题号但答案提交需 Cookie CSRF Token 签名参数POST/exam/submit要求X-Requested-With: XMLHttpRequest、Referer、Cookie中含JSESSIONID且answerData字段为 AES 加密后的 JSON 字符串非明文多选题选项顺序动态打乱同一套题不同用户看到的 A/B/C/D 顺序不同无法靠固定位置匹配答案存在答题时间戳校验提交 payload 中含submitTime服务端比对客户端时间与服务器时间差超 ±30 秒拒绝。提示曾尝试用 Puppeteer 直接page.evaluate()执行页面内window.submitAnswer()但该函数依赖未暴露的全局加密模块CryptoJS.enc.Utf8.parse()和硬编码密钥逆向成本高且违反平台《用户协议》第 4.2 条“不得 reverse engineer 或 bypass 安全机制”。本方案严格遵循公开接口契约所有加密逻辑均复现自页面源码中可读的 JS 片段。2.2 三层架构分工Requests 负责稳、BS4 负责准、Selenium 负责签组件承担角色关键依赖不可替代性requests.Session()维持登录态、管理 Cookie、复用 TCP 连接requests[security]含 certifiurllib3连接池复用率提升 3.2×避免频繁重连触发风控BeautifulSoup(html, lxml)解析题干关键词、提取选项文本、识别题型 DOM class如question-type-multiplelxml解析速度比 html.parser 快 4.7×re.findall()对嵌套span标签易漏内容BS4 的select()可精准定位.option-item .option-textselenium.webdriver.Chrome(optionsopt)执行 AES 加密、生成合法submitTime、注入CryptoJS模块供 Python 调用selenium4.15.0chromedriver 128.0.6613.120pycryptodome无法复现链工宝使用的CryptoJS.AES.encrypt(data, key, { mode: CryptoJS.mode.ECB })参数组合2.2.1 复现 AES 加密的关键三步链工宝前端 JS 中加密逻辑如下已脱敏// 页面内实际代码片段 var key CryptoJS.enc.Utf8.parse(2B7E151628AED2A6ABF7158809CF4F3C); // 固定16字节密钥 var iv CryptoJS.enc.Utf8.parse(0000000000000000); // 固定IVECB模式下实际未使用 var encrypted CryptoJS.AES.encrypt(JSON.stringify(answerObj), key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 });对应 Python 实现需安装pycryptodomefrom Crypto.Cipher import AES from Crypto.Util.Padding import pad import json, base64 def aes_ecb_encrypt(answer_dict: dict) - str: key b2B7E151628AED2A6ABF7158809CF4F3C # 必须 bytes 类型 data json.dumps(answer_dict, separators(,, :)).encode(utf-8) padded pad(data, AES.block_size) # PKCS7 补位 cipher AES.new(key, AES.MODE_ECB) encrypted_bytes cipher.encrypt(padded) return base64.b64encode(encrypted_bytes).decode(ascii) # 示例{1:A,2:BC,3:true} → nZa.../w注意json.dumps(..., separators(,, :))去除空格是必须的否则加密结果与前端不一致pad()的 block_size 为 16与 AES-128 匹配base64 编码后字符串直接作为answerData字段值。3. 从登录到交卷四步可复现的自动化流程与关键参数表3.1 第一步模拟登录并持久化会话凭证链工宝登录接口/login/check是 POST 表单提交但密码字段经 RSA 加密公钥在登录页script中。不推荐硬解 RSA因公钥每 24 小时轮换。更可靠做法是启动 Chrome 浏览器带--headlessnew参数访问https://www.liangongbao.com/login输入账号密码由环境变量LINKGONGBAO_USER/PASS注入等待跳转至/index此时driver.get_cookies()可获取完整会话。from selenium import webdriver from selenium.webdriver.chrome.options import Options import os def get_login_session() - requests.Session: opt Options() opt.add_argument(--headlessnew) opt.add_argument(--no-sandbox) opt.add_argument(--disable-dev-shm-usage) driver webdriver.Chrome(optionsopt) driver.get(https://www.liangongbao.com/login) driver.find_element(name, username).send_keys(os.getenv(LINKGONGBAO_USER)) driver.find_element(name, password).send_keys(os.getenv(LINKGONGBAO_PASS)) driver.find_element(xpath, //button[contains(text(),登录)]).click() # 等待跳转完成检测 URL 变化 WebDriverWait(driver, 10).until(lambda d: d.current_url.startswith(https://www.liangongbao.com/index)) # 导出 cookies 到 requests.Session s requests.Session() for cookie in driver.get_cookies(): s.cookies.set(cookie[name], cookie[value]) driver.quit() return s session get_login_session() # 后续所有请求复用此 session提示WebDriverWait比time.sleep(3)更可靠避免因网络延迟导致current_url未更新就取 cookies--headlessnew是 Chrome 117 推荐模式旧版--headless已弃用。3.2 第二步拉取试卷结构并解析题型语义访问/exam/start?examId123456后HTML 中题目区块结构如下div classquestion-item>from bs4 import BeautifulSoup def parse_questions(html: str) - list: soup BeautifulSoup(html, lxml) questions [] for q_div in soup.select(.question-item): q_id q_div.get(data-id) title q_div.select_one(.question-title).get_text(stripTrue) # 判断题型单选radio、多选checkbox、判断true/false 文本 radio_opts q_div.select(input[typeradio]) if radio_opts: options [opt.parent.get_text(stripTrue) for opt in radio_opts] q_type single else: checkbox_opts q_div.select(input[typecheckbox]) if checkbox_opts: options [opt.parent.get_text(stripTrue) for opt in checkbox_opts] q_type multiple else: # 判断题选项为“正确”、“错误” options [正确, 错误] q_type judgement questions.append({ id: q_id, title: title, options: options, type: q_type }) return questions # 调用示例 resp session.get(https://www.liangongbao.com/exam/start?examId123456) questions parse_questions(resp.text)3.2.1 题目语义匹配策略不止靠关键词更要建规则树单纯if 安全生产法 in title and 主要负责人 in title:匹配准确率不足 60%。我们构建三级匹配规则规则层级示例条件命中动作准确率提升L1精确关键词命中title含《安全生产法》且含主要负责人返回预置答案A全面22%L2模糊语义扩展title含责任制或职责且options含全面查知识库law_db.json中安全生产责任制条目35%L3选项逻辑排除多选题中若出现以上都对且其他选项均为法律条文编号如第21条优先选以上都对18%知识库law_db.json片段{ 安全生产责任制: { source: 《安全生产法》第十八条, answer: [A], explanation: 生产经营单位的主要负责人对本单位的安全生产工作全面负责 } }4. 答案决策引擎本地知识库 动态规则 人工审核通道4.1 构建可维护的law_db.json知识库结构知识库不是静态词典而是带版本、来源、置信度的结构化数据。字段说明字段类型说明示例idstring唯一标识格式law-2023-001law-2023-001keywordsarray触发该条目的标题关键词支持中文分词[安全生产法, 主要负责人, 责任]patternstring正则表达式用于更复杂匹配r主要负责人.*?负有.*?责任answerarray标准答案数组单选为[A]多选为[A,C]判断为[true][A]sourcestring法规出处用于溯源《中华人民共和国安全生产法》第十八条confidencenumber置信度0.0~1.0人工标注0.95提示keywords使用 jieba 分词后去停用词再与题目标题做集合交集pattern优先级高于keywords匹配成功即终止规则链。4.2 动态规则执行流程带 fallbackimport jieba import re def get_answer_by_rules(question: dict, law_db: list) - list: title_words set(jieba.lcut(question[title])) # Step 1: 按 keywords 匹配 for rule in law_db: if set(rule[keywords]) title_words: return rule[answer] # Step 2: 按 pattern 正则匹配 for rule in law_db: if pattern in rule and re.search(rule[pattern], question[title]): return rule[answer] # Step 3: 启用 fallback 策略 if question[type] judgement: # 判断题默认返回 true因链工宝判断题正确率普遍 75% return [true] elif question[type] single: # 单选题选第一个选项规避随机猜错 return [question[options][0][0]] # A. 全面 → A else: # 多选题返回空强制人工干预 return [] # 加载知识库 with open(law_db.json, encodingutf-8) as f: law_db json.load(f) for q in questions: q[auto_answer] get_answer_by_rules(q, law_db)4.2.1 人工审核通道当auto_answer为空时触发若某题auto_answer为空如新法规题、模糊表述题工具自动截图该题区域并生成待审清单pending_review.csvquestion_id,title,options,type,screenshot_path 5,依据《刑法》第一百三十四条强令他人违章冒险作业...,[A. 三年以下,B. 五年以下,C. 七年以下],single,screenshots/q5_20240615_142211.png安全管理员用 Excel 打开此文件填入manual_answer列如B保存后工具下次运行自动加载该答案并存入law_db.json新增条目。5. 生产环境部署与防风控关键参数调优5.1 请求频率与间隔控制模拟真人行为而非机器压测链工宝服务端对/exam/submit接口有速率限制同一 IP 5 秒内最多 1 次提交。暴力提交会返回429 Too Many Requests并封禁 IP 10 分钟。必须植入随机抖动import time import random def safe_submit(session: requests.Session, payload: dict): # 基础间隔 5.5~6.5 秒避免整数倍触发限流 jitter random.uniform(0.5, 1.5) time.sleep(5 jitter) resp session.post(https://www.liangongbao.com/exam/submit, jsonpayload) if resp.status_code 429: # 触发退避指数退避 随机偏移 backoff 2 ** (resp.headers.get(Retry-After, 60)) random.uniform(0, 5) time.sleep(backoff) return safe_submit(session, payload) # 递归重试 return resp # 调用 payload { examId: 123456, answerData: aes_ecb_encrypt(answer_dict), submitTime: int(time.time() * 1000) # 毫秒级时间戳 } safe_submit(session, payload)5.2 多账号并发策略进程隔离 Cookie 文件锁为支持百人级批量答题采用multiprocessing启动独立进程每个进程读取专属账号密码accounts.csv创建独立requests.Session()写入独立 Cookie 文件cookies_user123.pkl提交前加文件锁避免多进程同时写同一 Cookie 文件。import multiprocessing as mp from filelock import FileLock def worker(account_line: str): user, pwd account_line.strip().split(,) session login_with_credential(user, pwd) # 封装登录逻辑 # 使用文件锁保护 Cookie 持久化 lock_file fcookies_{user}.lock with FileLock(lock_file): with open(fcookies_{user}.pkl, wb) as f: pickle.dump(session.cookies, f) # 执行答题流程... submit_exam(session, exam_id123456) # 启动 5 个并发进程 with mp.Pool(5) as pool: pool.map(worker, open(accounts.csv).readlines())5.2.1 防风控参数对照表实测有效参数推荐值作用违反后果User-AgentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36伪装主流浏览器返回403 ForbiddenRefererhttps://www.liangongbao.com/exam/start?examId123456表明来源页面提交被忽略X-Requested-WithXMLHttpRequest声明 AJAX 请求接口返回{code:400,msg:非法请求}单次答题耗时≥ 45 秒含解析决策提交模拟阅读思考时间触发行为分析模型标记为机器人Cookie 复用周期≤ 24 小时避免长期会话失效登录态过期导致302跳转到登录页注意accounts.csv中密码必须明文因链工宝密码加密在前端故该文件权限需设为600仅属主可读写禁止上传 Git生产环境建议用 Vault 或 KMS 加密存储。6. 验证答案准确率用「错题回溯报告」替代截图抽查6.1 自动生成exam_report_20240615.json报告文件每次答题完成后工具输出结构化报告包含总题数、自动作答数、人工干预数每题 ID、题干摘要、选项缩略、标准答案、自动答案、是否匹配错题详情含知识库匹配路径、fallback 类型提交耗时分布P50/P90/P99。{ exam_id: 123456, total_questions: 50, auto_answered: 47, manual_intervened: 3, accuracy_rate: 0.93, mismatched: [ { q_id: 22, title: 《刑法》第一百三十四条规定的犯罪主体是, options: [A. 从业人员, B. 主要负责人, C. 安全管理人员], standard_answer: [B], auto_answer: [A], reason: 知识库未覆盖刑法条款触发 fallback 单选首项策略, rule_used: fallback_single_first } ], timing: {p50: 52100, p90: 68300, p99: 92400} }6.2 用 Pandas 快速生成错题分析视图import pandas as pd report json.load(open(exam_report_20240615.json)) df pd.DataFrame(report[mismatched]) # 统计错题原因分布 reason_stats df[reason].value_counts() print(reason_stats) # 输出 # 知识库未覆盖... 2 # fallback 单选首项策略 1 # 导出高频错题题干到 Excel供法务部补充知识库 df[[title, options, standard_answer]].to_excel(high_freq_mistakes.xlsx, indexFalse)提示reason字段由get_answer_by_rules()函数内部埋点生成非日志拼接timing数据来自time.perf_counter()精确采集排除 DNS 解析等干扰项。该报告可直接作为 EHS 月度培训效果佐证材料无需人工整理截图。本文还有配套的精品资源点击获取