1. 项目背景与需求分析
每次期末考试前,最让人头疼的就是整理复习资料。去年我在使用长江雨课堂时,发现老师布置的练习题散落在不同章节,复习时需要反复跳转页面,效率极低。于是萌生了一个想法:能不能把这些题目自动收集起来,建立本地习题库?
这个需求看似简单,但实际操作涉及多个技术环节:
- 登录认证:需要模拟浏览器行为携带Cookie
- 数据定位:从复杂的JSON结构中精准提取题目信息
- 异常处理:区分选择题和填空题的数据结构差异
- 数据存储:设计合理的MySQL表结构
我花了两个周末时间,最终实现了这个自动化系统。下面就把整个开发过程拆解成可复用的技术方案,特别适合有Python基础但刚接触爬虫的同学。
2. 环境准备与工具选型
2.1 基础环境配置
建议使用Python 3.8+版本,太新的版本可能会遇到库兼容问题。我的开发环境配置如下:
pip install requests==2.26.0 pip install lxml==4.6.3 pip install pymysql==1.0.2为什么选择这些版本?实测发现:
- requests 2.26.0在处理长连接时最稳定
- lxml 4.6.3的XPath解析成功率最高
- pymysql 1.0.2与MySQL 8.0兼容性最好
2.2 浏览器调试工具使用技巧
按F12打开开发者工具后,重点看这两个面板:
- Network面板:勾选"Preserve log"防止页面跳转丢失请求记录
- Elements面板:使用Ctrl+F搜索题目关键词定位DOM节点
一个小技巧:在请求头复制Cookie时,建议用"Copy as cURL"功能,然后到https://curlconverter.com/网站转换成Python代码,比手动复制更可靠。
3. 爬虫核心实现
3.1 登录认证机制
长江雨课堂采用Cookie验证,获取方式分三步:
- 正常登录网页版
- 打开任意课程页面
- 在Network标签找到"detlist"接口请求
关键代码实现:
headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'Cookie': 'your_cookie_here' # 建议定期更新 }注意:Cookie有效期通常为7天,建议写个自动检测机制。我后来改进的方案是当收到403响应时,自动弹出浏览器窗口要求重新登录。
3.2 数据结构解析
API返回的JSON结构非常复杂,主要数据路径如下:
data → problem_results → slide → ProblemBodys[0] → Paragraphs[0] → Lines[0] → Html (题目) Problem → Bullets[] → Contents[] (选项)解析时的几个坑:
- 列表索引可能越界,建议先判断长度
- Html字段包含XML格式数据,需要用XPath二次解析
- 填空题没有Bullets字段,直接访问会报KeyError
改进后的健壮性代码:
try: question_html = i['slide']['ProblemBodys'][0]['Paragraphs'][0]['Lines'][0]['Html'] question = etree.HTML(question_html).xpath('//span/text()')[0] except (IndexError, KeyError): continue # 跳过异常题目4. 数据存储方案
4.1 数据库设计
经过多次迭代,我最终采用的表结构如下:
CREATE TABLE `question_bank` ( `id` INT NOT NULL AUTO_INCREMENT, `question` VARCHAR(255) NOT NULL COMMENT '题目内容', `question_type` ENUM('choice','fill') NOT NULL COMMENT '题型', `options` TEXT COMMENT '选项JSON格式', `answer` VARCHAR(50) NOT NULL, `course_id` VARCHAR(20) NOT NULL COMMENT '关联课程', `create_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_question` (`question`(100)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4这个设计的优势:
- 使用utf8mb4编码支持emoji等特殊字符
- 增加题型字段方便后续扩展
- 选项存储为JSON格式更灵活
4.2 批量插入优化
直接循环执行INSERT效率太低,我改用executemany批量操作:
data = [(q1,ans1,opt1), (q2,ans2,opt2)] cursor.executemany( "INSERT INTO question_bank VALUES (%s,%s,%s)", data ) db.commit()实测1万条记录的插入时间从3分钟降到5秒,性能提升36倍。
5. 异常处理与调试
5.1 常见错误排查
Cookie失效:表现为返回403状态码
- 解决方案:实现自动刷新机制
JSON解析错误:通常是API返回非JSON数据
try: data = response.json() except ValueError: print("Invalid JSON:", response.text[:200])XPath匹配失败:HTML结构可能有变
- 建议:先用浏览器验证XPath有效性
5.2 日志记录方案
添加日志功能能极大提升调试效率:
import logging logging.basicConfig( filename='spider.log', level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s' )记录关键操作:
logging.info(f"成功抓取{len(questions)}道题目") logging.warning(f"跳过异常题目: {traceback.format_exc()}")6. 项目扩展思路
6.1 定时自动同步
用APScheduler实现每天凌晨自动同步:
from apscheduler.schedulers.blocking import BlockingScheduler sched = BlockingScheduler() @sched.scheduled_job('cron', hour=2) def auto_sync(): # 执行爬虫逻辑 sched.start()6.2 可视化查询界面
用Flask快速搭建Web界面:
from flask import Flask app = Flask(__name__) @app.route('/questions') def list_questions(): # 查询数据库返回JSON6.3 移动端适配
将数据导出为Anki记忆卡格式,利用间隔重复算法提升记忆效率。
这个项目从最初的简单爬虫,逐步发展成完整的习题管理系统。最大的收获不是技术本身,而是学会如何把一个实际需求拆解成可执行的技术方案。过程中遇到的每个报错都是最好的学习机会,现在回头看最初的代码,发现至少有10处可以优化的地方。