在这篇博客中,我们将深入解析一个基于Python的自动化登录系统的实现。该系统能够自动登录到某个教务系统页面,获取课程表和成绩信息,并将成绩保存到CSV文件中。本文将逐步讲解代码的各个模块及其功能。
1. 项目介绍
项目的主要目标是通过模拟登录教务系统,实现自动化操作,包括验证码识别、密码加密、登录页面获取、课程表查询以及成绩查询。整个项目基于Python实现,使用了requests
库进行HTTP请求,lxml
库进行HTML解析,以及一个验证码识别客户端库CaptureRecognition
。
2. 环境准备
首先,确保你已安装以下Python库:
pip install requests lxml
此外,项目中还用到了一个外部的Node.js脚本来处理密码的加密,这需要在系统中安装Node.js环境。
超级鹰平台自动输入验证码(可通过训练模型对验证码进行识别)
3. 项目结构
项目包含以下主要模块:
- LoginAutomation类: 包含整个自动化流程的核心逻辑。
- get_login_page方法: 负责获取登录页面的初始数据。
- get_captcha_code方法: 处理验证码的获取和识别。
- get_rsa_params方法: 获取RSA加密所需的公钥参数。
- get_encrypted_password方法: 调用外部Node.js脚本进行密码加密。
- post_login方法: 执行登录请求并验证登录结果。
- query_courses方法: 获取课程表信息。
- parse_course_data方法: 解析并打印课程表数据。
- query_score方法: 获取成绩信息。
- parse_score_data方法: 解析并保存成绩数据到CSV文件。
- save_to_csv方法: 将成绩数据保存到CSV文件。
4. 核心代码解析
4.1. 初始化与配置加载
首先,我们定义了LoginAutomation
类的初始化方法,用于配置基本信息,如用户名、密码、教务系统的基URL等。
def __init__(self, base_url, username, password, captcha_client):
self.base_url = base_url
self.username = username
self.password = password
self.captcha_client = captcha_client
self.sess = requests.Session()
self.timestamp = int(time.time() * 1000)
self.csrf_token = None
self.rsa_keys = None
配置文件config.json
会加载用户的配置信息,包括用户名、密码和验证码识别系统的账号信息。
def load_config():
with open('config.json', 'r') as f:
config = json.load(f)
return config
4.2. 获取登录页面和CSRF Token
在登录前,需要先获取页面的CSRF Token
。这通过解析登录页面的HTML实现:
def get_login_page(self):
url = f"{self.base_url}/xtgl/login_slogin.html"
response = self.sess.get(url=url, allow_redirects=True)
tree = etree.HTML(response.text)
self.csrf_token = tree.xpath('//input[@id="csrftoken"]/@value')[0]
logging.info("获取登录页面成功")
4.3. 获取并识别验证码
验证码是登录系统的一个重要防护措施。我们使用CaptureRecognition
库来自动识别验证码:
def get_captcha_code(self):
url = f"{self.base_url}/kaptcha?time={self.timestamp}"
response = self.sess.get(url=url, allow_redirects=True)
if response.status_code == 200:
with open('captcha.png', 'wb') as f:
f.write(response.content)
logging.info("验证码图片获取成功")
return self.captcha_client.PostPic(open('captcha.png', 'rb').read(), 1902)['pic_str']
else:
logging.error(f"获取验证码图片失败,状态码:{response.status_code}")
return None
4.4. 获取RSA加密参数并加密密码
为了保证密码的安全性,系统使用RSA加密。我们首先通过请求获取RSA的公钥参数,然后使用外部Node.js脚本对密码进行加密:
def get_rsa_params(self):
url = f"{self.base_url}/xtgl/login_getPublicKey.html?time={self.timestamp}&_={self.timestamp - 60}"
response = self.sess.get(url=url)
if response.status_code == 200:
self.rsa_keys = response.json()
modulus = self.rsa_keys.get('modulus')
exponent = self.rsa_keys.get('exponent')
with open('keys.json', 'w') as file:
json.dump({'modulus': modulus, 'exponent': exponent}, file)
file.close()
logging.info("获取RSA参数成功")
else:
logging.error(f"获取RSA参数失败,状态码:{response.status_code}")
def get_encrypted_password(self):
result = subprocess.run(['node', 'fetch.js'], capture_output=True, text=True)
if result.returncode == 0:
logging.info("密码加密成功")
return result.stdout.strip()
else:
logging.error(f"密码加密失败:{result.stderr}")
return None
4.5. 提交登录请求
通过post_login
方法提交登录请求,验证登录是否成功:
def post_login(self, encrypted_password, captcha_code):
url = f"{self.base_url}/xtgl/login_slogin.html?time={self.timestamp}"
data = [
('csrftoken', self.csrf_token),
('language', 'zh_CN'),
('yhm', self.username),
('mm', encrypted_password),
('yzm', captcha_code)
]
response = self.sess.post(url=url, data=data, allow_redirects=True)
if "学生课表查询" in response.text:
logging.info("登录成功")
return True
else:
logging.error("登录失败")
return False
4.6. 查询课程表与成绩
登录成功后,可以查询课程表与成绩,并将成绩保存到CSV文件中:
def query_courses(self):
url = f"{self.base_url}/kbcx/xskbcx_cxXsgrkb.html"
data = {
'xnm': '2023',
'xqm': '12',
'kzlx': 'ck',
'xsdm': ''
}
response = self.sess.post(url=url, data=data)
response.encoding = 'utf-8'
return response.json()
def parse_course_data(self, json_data):
kb_list = json_data['kbList']
logging.info("课程列表:")
for kb_item in kb_list:
logging.info(
f"课程名称: {kb_item['kcmc']}, 教室: {kb_item['cdmc']}, 节次: {kb_item['jc']}, 教师: {kb_item['xm']}, 周次: {kb_item['zcd']}, 星期: {kb_item['xqjmc']}")
查询成绩并将其保存到CSV文件中:
def query_score(self):
url = f"{self.base_url}/cjcx/cjcx_cxXsgrcj.html?doType=query"
data = {
'xnm': '2024',
'xqm': '12',
'kcbj': '',
'_search': 'false',
'nd': f'{self.timestamp}',
'queryModel.showCount': '15',
'queryModel.currentPage': '1',
'queryModel.sortName': '',
'queryModel.sortOrder': 'asc',
'time': '1'
}
response = self.sess.post(url=url, data=data)
response.encoding = 'utf-8'
return response.json()
def parse_score_data(self, json_data):
items_list = json_data['items']
logging.info("成绩列表:")
course_data = []
for cj_item in items_list:
logging.info(
f"成绩: {cj_item['cj']}, 绩点: {cj_item['jd']}, 教师: {cj_item['jsxm']}, 考试性质: {cj_item['ksxz']}, 课程名称: {cj_item['kcmc']}, 课程类别: {cj_item['kcxzmc']}")
course_data.append([
cj_item['cj'],
cj_item['jd'],
cj_item['jsxm'],
cj_item['ksxz'],
cj_item['kcmc'],
cj_item['kcxzmc']
])
self.save_to_csv(course_data)
@staticmethod
def save_to_csv(course_data):
file_path = 'scores.csv'
is_empty = not os.path.exists(file_path) or os.path.getsize(file_path) == 0
with open(file_path, mode='a', newline='', encoding='utf-8') as file:
writer = csv.writer(file)
if is_empty:
writer.writerow(['成绩', '绩点', '教师', '考试性质', '课程名称', '课程类别'])
writer.writerows(course_data)
5. 项目运行
将上述代码保存为Python脚本后,确保配置
文件和外部Node.js脚本已经准备好,运行主程序即可完成自动化登录及成绩查询:
if __name__ == "__main__":
config = load_config()
captcha_client = Chaojiying_Client(
config['chaojiying_user'],
config['chaojiying_pass'],
config['chaojiying_soft_id']
)
login_automation = LoginAutomation(
base_url="教务网地址",
username=config['username'],
password=config['password'],
captcha_client=captcha_client
)
login_automation.run()
效果展示
6. 总结
这篇博客介绍了如何使用Python实现一个自动化登录系统,并且完成了从验证码识别到成绩保存的整个流程。通过这个项目,你可以学习到许多网络爬虫的实战技巧,例如请求伪装、验证码识别、RSA加密等。
希望这篇文章对你有所帮助,欢迎在实践中改进和扩展此项目!