3步搞定山西省干部在线学习,面试必问避坑指南
版本升级后 API 全变了,昨天还能跑的脚本今天直接报错,这种崩溃感谁懂?在山西省干部在线学习系统的实际接入中,很多开发者发现旧版接口文档已经失效,新版的鉴权方式和数据返回结构发生了根本性变化。这不仅是技术难题,更是面试必问的实战场景,考察的是你对系统迭代后的快速适应能力。
别慌,咱们不整虚的。今天直接上代码,从项目搭建到核心逻辑,手把手带你从零复现一个能够对接山西省干部在线学习平台的自动化辅助工具。这不是为了作弊,而是为了理解平台数据交互的逻辑,顺便解决批量下载、学时统计等实际痛点。
项目目标与痛点分析
咱们先明确要解决什么。山西省干部在线学习平台(以下简称“晋干”)主要面向党政机关干部,提供课程学习、考试测评等功能。对于技术从业者或相关系统的维护人员来说,核心需求通常集中在三点:登录态保持:平台 Token 过期机制较严,手动刷新繁琐。
课程进度同步:需要准确记录视频播放进度,确保学时合规。
数据可视化:将散落在各页面上的学习记录导出为结构化数据,便于分析。痛点在于,平台前端代码混淆度高,直接逆向接口难度极大。我们需要通过模拟用户行为,抓取关键接口参数,构建一个轻量级的客户端。
注意:本文仅探讨技术实现原理,请遵守平台服务条款,严禁用于非法用途。
目录结构设计
一个清晰的项目结构是工程化的基础。我们采用 Python 3.9+ 环境,结合 requests 处理 HTTP 请求,bs4 解析 HTML,pandas 处理数据。
shanxi_ganbu_client/
├── config.py # 配置文件,存储账号、Cookie 模板
├── main.py # 入口文件,协调各模块
├── core/
│ ├── __init__.py
│ ├── auth.py # 登录与 Token 管理
│ ├── api.py # 接口请求封装
│ └── parser.py # 数据解析逻辑
├── utils/
│ ├── __init__.py
│ ├── logger.py # 日志记录
│ └── helpers.py # 工具函数
├── data/ # 存放导出数据
└── requirements.txt # 依赖包这种分层结构的好处是,当平台接口变动时,你只需要修改 core/api.py 中的 URL 或参数,而不需要动主逻辑。这也是面试必问中关于“代码可维护性”的高频考点。
核心代码实现
1. 配置与环境准备
首先,创建 requirements.txt,安装必要依赖:
requests==2.31.0
beautifulsoup4==4.12.2
pandas==2.0.3
lxml==4.9.3在 config.py 中定义基础配置。这里使用字典存储敏感信息,实际生产环境建议改用环境变量。
import osCONFIG = {base_url: https://www.sxgbdx.cn, # 假设域名,实际需替换login_path: /user/login,course_list_path: /api/course/list,user_agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36,# 从浏览器 F12 控制台复制初始 Cookieinitial_cookies: {JSESSIONID: your_session_id_here,auth_token: your_token_here}
}2. 登录与 Token 管理
平台通常采用 Session + Token 双机制。我们需要先通过 POST 请求登录,获取最新的 Cookie,然后提取 Token 用于后续 API 调用。
在 core/auth.py 中实现登录逻辑:
import requests
from config import CONFIGclass AuthManager:def __init__(self):self.session = requests.Session()self.session.headers.update({User-Agent: CONFIG[user_agent],Accept: application/json, text/plain, */*})# 设置初始 Cookie,绕过部分基础验证for key, value in CONFIG[initial_cookies].items():self.session.cookies.set(key, value)def login(self, username: str, password: str) - bool:模拟登录过程:param username: 账号:param password: 密码:return: 登录成功返回 Trueurl = f{CONFIG['base_url']}{CONFIG['login_path']}payload = {username: username,password: password,captcha: # 若有验证码,需接入打码平台或手动输入}try:response = self.session.post(url, json=payload, timeout=10)response.raise_for_status()result = response.json()# 判断登录状态,不同平台字段不同,需根据实际返回调整if result.get(code) == 200:print(登录成功,更新 Cookie...)# 登录成功后,服务器通常会 Set-Cookie,requests 会自动保存return Trueelse:print(f登录失败: {result.get('msg')})return Falseexcept requests.RequestException as e:print(f请求异常: {e})return False关键点:requests.Session 会自动处理 Cookie 的持久化,这是很多新手容易忽略的。如果你用 requests.post 直接调用,每次请求都是独立的,Cookie 会丢失。
3. API 请求封装
登录成功后,我们需要调用课程列表接口。在 core/api.py 中封装通用的 GET 请求方法:
import requests
from config import CONFIG
from .auth import AuthManagerclass APIClient:def __init__(self, auth_manager: AuthManager):self.auth = auth_managerself.session = auth_manager.sessiondef get_course_list(self, page: int = 1, size: int = 20) - list:获取课程列表:param page: 页码:param size: 每页数量:return: 课程数据列表url = f{CONFIG['base_url']}{CONFIG['course_list_path']}params = {page: page,size: size,type: video # 假设类型参数}# 某些接口需要额外 Header 中的 Tokentoken = self.session.cookies.get(auth_token)if token:self.session.headers[Authorization] = fBearer {token}try:response = self.session.get(url, params=params, timeout=10)response.raise_for_status()data = response.json()# 提取数据部分if data.get(code) == 200:return data.get(data, {}).get(list, [])return []except requests.RequestException as e:print(f获取课程列表异常: {e})return []避坑指南:在 Stack Overflow 上搜索类似 API 调试问题时,你会发现大量关于 403 Forbidden 的讨论。这通常不是因为 IP 被封,而是 Header 中缺少必要的 Referer 或 Origin。在 self.session.headers 中补充这两个字段,往往能解决 80% 的权限问题。
4. 数据解析与存储
获取到 JSON 数据后,我们需要将其转换为 DataFrame,方便后续导出 Excel。在 core/parser.py 中实现:
import pandas as pd
from datetime import datetimeclass DataParser:@staticmethoddef courses_to_dataframe(courses: list) - pd.DataFrame:将课程列表转换为 DataFramerecords = []for course in courses:record = {id: course.get(id),title: course.get(title),duration: course.get(duration), # 时长,单位分钟progress: course.get(progress, 0), # 当前进度百分比last_access: course.get(lastAccessTime),category: course.get(categoryName)}records.append(record)df = pd.DataFrame(records)# 格式化时间字段if 'last_access' in df.columns:df['last_access'] = pd.to_datetime(df['last_access'], errors='coerce')return df在 main.py 中整合所有模块:
import pandas as pd
from core.auth import AuthManager
from core.api import APIClient
from core.parser import DataParserdef main():# 1. 初始化认证auth = AuthManager()if not auth.login(your_username, your_password):return# 2. 初始化 API 客户端api = APIClient(auth)# 3. 获取数据all_courses = []page = 1while True:print(f正在获取第 {page} 页课程...)courses = api.get_course_list(page=page, size=20)if not courses:breakall_courses.extend(courses)page += 1# 简单防爬策略,每次请求间隔 1 秒import timetime.sleep(1)# 4. 解析与保存if all_courses:df = DataParser.courses_to_dataframe(all_courses)filename = fdata/learning_records_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csvdf.to_csv(filename, index=False, encoding='utf-8-sig')print(f数据已保存至: {filename})else:print(未获取到任何课程数据)if __name__ == __main__:main()运行与测试
在执行代码前,确保你已经从浏览器中正确抓取了初始 Cookie。如果平台有图形验证码,上述代码中的 captcha 字段需要替换为实际验证码值。
运行 python main.py,观察控制台输出。如果提示“登录失败”,请检查:账号密码是否正确。
User-Agent 是否被识别为爬虫(某些平台会拦截非浏览器 UA)。
Cookie 是否过期。如果成功获取数据,data 目录下会生成 CSV 文件。用 Excel 打开,检查“进度”列是否准确。如果发现进度为 0,可能是接口返回的进度字段名发生了变化,需回到浏览器 F12 查看 Network 面板,对比最新响应结构。
测试技巧:在本地使用 mitmproxy 抓包,对比你代码中发出的请求与浏览器原生请求的差异。重点关注 Headers 和 Payload 部分。这是排查 API 变动最有效的方法。
优化扩展
当前版本仅实现了基础的数据获取。为了更贴近生产环境,可以做以下扩展:自动刷新 Token:设置定时任务,每隔 2 小时检查 Token 有效性,若即将过期则自动重新登录。
异常重试机制:在网络波动时,使用 tenacity 库实现指数退避重试。
数据监控:将进度数据存入 SQLite 数据库,生成趋势图表,监控学习进度是否达标。关于面试必问的延伸:如果面试官问你“如何处理高并发下的请求”,你可以提到引入 asyncio 和 aiohttp 进行异步请求,但要注意控制并发数,避免触发平台风控。
此外,对于继续教育的学时规定,不同省份差异较大。山西省通常要求每年完成一定学时的政治理论学习和业务能力提升课程。通过导出数据,你可以快速统计各类别的学时分布,确保符合规定。例如,使用 pandas 的 groupby 功能:
# 在 main.py 末尾添加
summary = df.groupby('category')['duration'].sum()
print(\n各类别学习时长统计 (分钟):)
print(summary)这能帮助你快速发现哪类课程学时不足,及时补课。
小结
本文带你从零搭建了一个对接山西省干部在线学习平台的 Python 客户端。我们覆盖了从环境配置、登录认证、API 请求到数据解析的完整流程。重点强调了 Session 对象在 Cookie 管理中的作用,以及如何通过抓包调试应对 API 变动。
技术工具是中性的,关键在于如何使用。对于备考或日常学习而言,自动化工具能极大提升效率,让你从繁琐的点击中解放出来,专注于内容本身。
在开发过程中,你遇到过哪些平台接口变动的“坑”?或者你有更高效的 Token 刷新策略吗?你更常用哪种写法处理异步请求?评论区交流。