当前位置: 首页 > news >正文

Python爬取科目一题库1685道并生成Word文档

Python爬取科目一题库1685道并生成Word文档

在准备机动车驾驶人理论考试(科目一)的过程中,很多人会遇到这样一个痛点:题库分散在网页上,每次只能手动点击“下一题”查看,复习效率低,打印也不方便。有没有办法一次性把全部题目抓下来,整理成一份结构清晰、可编辑的 Word 文档?答案是肯定的——通过 Python 编写一个轻量级爬虫,就能高效完成这个任务。

本文将带你从零开始,完整实现对某驾驶员考试网站科目一题库的自动化采集,并最终导出为.docx文件。整个过程不依赖 Selenium 模拟浏览器操作,而是直接分析其数据接口,精准构造请求,大幅提升爬取效率与稳定性。


环境准备与技术选型

我们优先选择简洁高效的工具链,避免过度工程化。开发环境建议使用Python 3.7+,推荐配合 Anaconda 创建独立虚拟环境以隔离依赖:

conda create -n crawler-env python=3.8 conda activate crawler-env

核心依赖如下:

pip install requests python-docx lxml urllib3
包名用途说明
requests发起 HTTP 请求获取 JSON 数据
python-docx创建和写入.docx格式文档
urllib3/requests下载图片资源
lxml可用于后续扩展 HTML 解析功能

开发工具推荐VS Code + Python 插件PyCharm,调试体验更流畅。


接口逆向:找到题库的真实数据源

打开目标页面 https://m.jsyks.com/km1_sxlx,你会发现每点一次“下一题”,题目就刷新一次。表面上看像是前端动态加载,但其实背后有规律可循。

利用 Chrome 浏览器开发者工具(F12),切换到Network面板,观察网络请求行为。很快就能发现,每当翻页时,浏览器会向类似以下格式的 URL 发起请求:

https://tkdata.mnks.cn/ExamData/{code}.json?CALL=?{version}.json

其中{code}是每道题的唯一编码,而{version}是当前题库版本号。这些信息并非随机生成,而是内嵌在页面源码或加载的 JS 脚本中。

进一步查看Sources面板中的 JavaScript 文件(如lianxiti.js2020_lianxi.js),可以定位到关键变量:

var ExamVersion = "20201231143735"; // 题库版本 var ExamCount = 1685; // 总题数 var ExamCodes = "02dec,0d977,..."; // 所有题目的编码列表

这意味着我们不需要模拟用户点击,只需提取这串ExamCodes并循环拼接 URL,即可批量获取所有题目数据。这是一种典型的“静态 API 接口暴露”模式,非常适合用脚本直接调用。


数据结构解析:判断题 vs 选择题

每个请求返回的是一个 JSON 对象,结构非常简洁。例如一道判断题的响应如下:

{ "tkId": 2013, "sortId": 1501, "code": "3fd79", "tx": 1, "tm": "驾驶机动车在道路上违反道路通行规定应当接受相应的处罚。", "da": "对", "tags": "违反道路通行规定" }

字段含义明确:
-tm: 题干文本(部分含 Unicode 转义)
-da: 正确答案(“对”或“错”)
-tv: 图片标识(存在则表示该题附带配图)

对于选择题,tm字段通常包含<br/>分隔符来表示换行选项,例如:

"tm": "夜间驾驶机动车在照明条件良好的路段行驶时,应使用什么灯光?<br/>近光灯<br/>远光灯<br/>危险报警闪光灯<br/>雾灯"

我们可以用.split('<br/>')将其拆分为问题描述和多个选项,处理起来也很直观。

实际传输中某些字符串可能经过 Unicode 编码(如\u9a7e\u9a8f),Python 的requests.get().json()方法能自动解码,无需额外处理。


图片资源提取策略

如果某道题包含图片,其字段tv值形如/SU/od50vqgFW8x。去掉前缀/SU/后,拼接到固定域名即可得到真实图片地址:

img_url = "https://sucimg.itc.cn/sblog/" + tv.split('/')[-1] # 示例结果:https://sucimg.itc.cn/sblog/od50vqgFW8x

该链接可以直接通过requests.get()获取二进制内容并保存为本地文件。考虑到图片数量较多,建议统一存入./images/目录,并按唯一 key 命名。

值得注意的是,这类图片服务器一般无强反爬机制,短间隔请求基本不会被封 IP,但仍建议加入轻微延时(如 0.5 秒)以降低风险。


完整爬虫实现:从前端分析到文档输出

有了上述分析,就可以动手写代码了。以下是完整的实现逻辑,分为三步:参数初始化 → 遍历请求 → 写入 Word。

参数定义与文档创建

import requests import time import os from docx import Document from docx.shared import Pt from docx.enum.text import WD_PARAGRAPH_ALIGNMENT # 固定参数(来自JS脚本) ExamVersion = "20201231143735" ExamCount = 1685 ExamCodes_str = "02dec,0d977,02567,..." # 此处省略完整列表 ExamCodes = ExamCodes_str.split(',') # 创建Word文档 doc = Document() doc.add_heading('机动车驾驶人理论考试科目一题库(共1685题)', level=1) headers = {'Connection': 'close'} # 减少连接占用

图片下载函数

def download_image(img_url, img_name): try: resp = requests.get(img_url, timeout=30) if resp.status_code == 200: path = f"./images/{img_name}.png" os.makedirs("./images", exist_ok=True) with open(path, 'wb') as f: f.write(resp.content) return path return None except Exception as e: print(f"图片下载失败: {e}") return None

主循环:请求 + 解析 + 写入

for idx in range(ExamCount): code = ExamCodes[idx] url = f"https://tkdata.mnks.cn/ExamData/{code}.json?CALL=?{ExamVersion}.json" try: resp = requests.get(url, headers=headers, timeout=40) data = resp.json() # 处理题干与答案 tm = data['tm'].replace('\u3002', '。').replace('<br/>', '\n') da = data['da'] # 添加题目 p = doc.add_paragraph() p.add_run(f"{idx+1}. {tm}\n").font.size = Pt(12) # 加粗显示答案 ans_para = doc.add_paragraph() ans_para.add_run(f"答案: {da}").font.bold = True # 插入图片(如有) if data.get('tv'): tv = data['tv'] img_key = tv.split('/')[-1] img_url = f"https://sucimg.itc.cn/sblog/{img_key}" img_path = download_image(img_url, img_key) if img_path: pic_para = doc.add_paragraph() pic_para.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER run = pic_para.add_run() run.add_picture(img_path, width=Pt(300)) print(f"已处理第 {idx+1} 题: {tm[:30]}...") time.sleep(0.5) # 控制频率,防IP限制 except Exception as e: print(f"第 {idx+1} 题抓取失败: {e}") continue # 保存最终文档 output_path = "科目一题库1685道.docx" doc.save(output_path) print(f"\n✅ 全部题目已成功导出至: {output_path}")

运行完成后,你会得到一个包含全部题目、答案及配图的 Word 文档,支持搜索、打印、划重点,极大提升备考效率。


提升健壮性:引入AI辅助优化逻辑

虽然本次任务本身并不复杂,但在实际项目中,经常会遇到字段缺失、接口变动、异常重试等问题。这时候可以借助大模型进行智能补全。

例如使用VibeThinker-1.5B这类擅长代码推理的模型,输入提示:

How to safely extract fields from JSON when some keys may be missing?

它可能会返回一段符合 PEP8 规范的健壮代码:

def safe_get(data, key, default=""): return data.get(key, default) or ""

然后你可以将其应用到主流程中:

tm = safe_get(data, 'tm', '未知题目').replace('<br/>', '\n') da = safe_get(data, 'da', '无答案')

这种“人类设计框架 + AI 补充细节”的协作模式,正逐渐成为现代开发的新常态。尤其在处理边缘情况、错误处理、日志记录等非核心但重要的模块时,AI 能显著减少样板代码编写时间。


总结与延伸思考

本文展示了一个典型的静态 API 数据采集场景:通过分析前端行为,定位真实数据接口,绕过繁琐的 UI 操作,直接对接后端服务。这种方法不仅速度快、成功率高,而且资源消耗极低。

整个方案的核心思想是“最小必要原则”——不用 Selenium,不用 Puppeteer,不搞分布式调度,仅靠requests + python-docx两个轻量库就完成了全部工作。这种极简主义风格更适合个人学习、小规模数据整合等场景。

此外,类似的思路还可拓展至其他领域:
- 抓取在线课程题库生成复习资料
- 批量导出知乎专栏文章为 PDF
- 备份公开 API 提供的新闻/天气/股票数据

只要目标站点存在可预测的数据接口,哪怕没有开放文档,也能通过逆向手段实现自动化采集。

最后提醒一句:请仅将此类技术用于合法合规的学习用途,尊重网站版权与访问规则,合理控制请求频率,做一个有责任感的技术使用者。

http://www.jsqmd.com/news/144448/

相关文章:

  • 2.5D Generator 2.0:PS一键生成2.5D插画神器
  • 老化路径如何影响锂离子电池热失控?
  • 从零部署Open-AutoGLM:智谱开源模型源码获取与环境配置实战(全流程图解)
  • 解析 ‘Fail-stop’ 与 ‘Fail-safe’:内核设计中处理不可预测错误的两种哲学权衡
  • Linux Fundamentals Part 1 26.12.2025
  • 【AI】SWOT分析法
  • 【性能突破极限】:实测千元机运行Open-AutoGLM的惊人表现
  • 面试官问:为什么要用 PPO / DPO / GRPO?SFT 真的不够吗?
  • 2025智能硅砂蜂巢结构雨水回用系统TOP5权威推荐 - 工业品网
  • Scrum 的定义解读
  • 【AI开发干货】Text2SQL和RAG的区别不是技术栈,而是你对“问题本身“的理解!90%的程序员都理解错了!
  • 显卡性能设置指南:告别游戏卡顿
  • 研发周期缩短 60%!电鱼智能 RK3588“核心板+底板”模式助推渔具产品极速定制
  • Open-AutoGLM本地部署性能翻倍秘诀:CUDA、TensorRT协同优化实战
  • 金融数据分析面试题:SQL与业务理解实战
  • 基于PLC的双层立体车库电气控制系统设计
  • Open-AutoGLM能跑在虚拟机上吗:5大关键配置决定成败
  • 2025年舆情分析报告平台怎么选?蜜度旗下的新浪舆情通好用吗 - 深度智识库
  • 电磁风暴中的定海神针!电鱼智能 RK3568 强化 EMC 隔离设计,守护水面作业“零宕机”
  • 2025连接器超声波焊接机厂家推荐:新能源超声波焊接机/汽车配件超声波焊接机/继电器超声波焊接机源头厂家精选 - 品牌推荐官
  • 【Open-AutoGLM架构深度解析】:揭秘下一代自动化大语言模型框架核心技术
  • 发现并分析一款PHP木马后门程序
  • 自动驾驶—CARLA仿真(28)地图与导航(Maps and navigation)
  • 企业与个人必看:北京债务纠纷律所TOP5榜单发布,谁才是真正的债权守护者? - 老周说教育
  • 光栅衍射主极大个数与大学物理光学解析
  • 安卓用户必看:Open-AutoGLM安装失败?这7个坑你一定要避开
  • XR开发随笔(RhinoX Pro)
  • 内核技术问答:rcu_boost技术以及CPU静止状态与任务静止状态的区别
  • PI技术路径转向human data?澄清和解读
  • 显卡性能设置指南:解决游戏卡顿掉帧问题