​ 前几天听到朋友说自己选课事情,突发奇想想要搞这样一个东西,但是由于各种原因只做到以下的完成度,具体的情况也会在解释的最后留下。这个只适用于曲师大的教务系统,因为用的这个系统来进行的一个调试,对于其他的系统,思路都是一样的,代码也只适用于学习,请不要用以其他用途!代码放在最后。

工具

  • Python3
  • selenium库(浏览器自动化操作)
  • ddddocr库(OCR图片文字识别)
  • time库(定时操作)

思路

​ 对于想要做到的这个需求呢,我选择的是python + selenium库进行一个浏览器自动化操作,在写的过程中因为发现了一个验证码的问题所以又用到了一个ddddocr库,如果想要定时操作的话再加上一个time库。之后就是理清操作的顺序就好了,想到于人工操作一遍的过程。

  1. 进入系统登录界面
  2. 进入用户界面
  3. 进入选课界面
  4. 开始选课

大的一个方向、思路就是上面这四步,具体的细化之后再谈及。

过程

进入系统登录界面

​ 这一步就很简单,直接用selenium库打开chormedriver就好了

        self.driver = webdriver.Chrome()
self.driver.maximize_window() # 最大化

进入用户界面

​ 这一步就是登录,要完成的就是把账号,密码输入对应的框,然后输入验证码,点击登录。

​ 首先把账号密码输入到框里很简单

        self.driver.find_element(By.ID, 'userAccount').send_keys(self.user_account)
self.driver.find_element(By.ID, 'userPassword').send_keys(self.user_password)

​ 直接调用selenium库中的函数就可以进行该操作

​ 接下来是这一步骤的一个问题,就是如何过验证码,因为该系统的验证码图片并不复杂,我想到的一个解决措施就是把验证码截图,然后识别图片上的信息,然后重复上述操作就好了。

​ 如何截图,我首先想到的是指定位置然后用某个库截图或者是打开图片的链接保存图片,后来发现每次点开链接图片都是不一样的(应该是JS的原因),最后发现原来selenium库自带一个元素截图的函数。

	    ocr = ddddocr.DdddOcr()
img = self.driver.find_element(By.ID, 'SafeCodeImg') # 定位到验证码图片
img.screenshot('code.png') # 给验证码元素截图并保存
with open('code.png', 'rb') as f:
img_bytes = f.read() # 读取图片编码
return ocr.classification(img_bytes) # 返回图片中的验证码

​ 这样的话只需要定位到这个图片元素,然后保存下来,再读取编码信息,使用ddddocr库来进行一个文字识别,返回图片的验证码模拟登陆就好了

​ 最后登录也就是找到元素然后调用函数模拟点击就好了

self.driver.find_element(By.XPATH, "//*[@class='btn btn-primary login_btn']").click()

进入选课界面

​ 这一部分本来难度不算很高,但是因为JS的原因,有一些地方需要注意。

​ 首先就是模拟点击一些摁扭可以转到选课界面的地方。因为这个选课系统转到一个页面以后会产生一些新的东西(框架),这时候我们要再找上面的元素就要分清楚在哪一个框架上。

    self.driver.switch_to.frame("Frame1")  # !!

​ 通过F12开发者选项可以找到这个元素所在的框架不是初始的框架(frame)而是在JS产生的一个新框架中,那么就需要用这个函数来转到新的框架Frame1(通过开发者选项找到的frame id)

​ 只有这一点需要特别注意!!!

开始选课

​ 首先一点,进入这个界面的时候,浏览器的界面是切换了的,因此我们也需要将selenium的定位切换到我们所看到的界面上。

        new_window = self.driver.window_handles[-1]
self.driver.switch_to.window(new_window)

​ 之后通过点击又转产生了一个新的框架,因此在进行一个转框架的操作。

​ 最后就是通过课程id、上课老师名字、上课是星期几三个来作为键确定唯一的课程,进行一个选择,并重复上述操作直到完成全部选课。

        for my_course in self.user_course:
self.driver.find_element(By.ID, 'kcxx').send_keys(my_course[1]) # 通过课程信息查找
self.driver.find_element(By.ID, 'skls').send_keys(my_course[2]) # 通过上课老师查找
self.driver.find_element(By.XPATH, f"//*[@id='skxq']/option[{my_course[3]+1}]").click()
# 选择星期(一 - 2, 二 - 3, 三 - 4, 四 - 5, 五 - 6)
self.driver.find_element(By.XPATH, "html/body/div[3]/input[6]").click()
self.driver.find_element(By.ID, 'kcxx').clear() # 清空课程查询框
self.driver.find_element(By.ID, 'skls').clear() # 清空上课老师框
time.sleep(1)
"""
没刷新出来加一个等待1秒
"""
course_remain = self.driver.find_element(By.XPATH, "//*[@class='odd']/td[9]").text # 查看课余量
if int(course_remain) > 0:
self.driver.find_element(By.XPATH, "//*[@class='odd']/td[11]/div/a").click()
"""
这里缺少了一部分确定的代码
"""
else:
print(f'{my_course[1]}选课失败')
time.sleep(3) # 3秒的暂停

附加功能

定时功能

​ 使用time库来进行一个定时开始执行

   run_time_h = 9  # 定时小时
run_time_m = 0 # 定时分钟
while True:
current_time = time.localtime(time.time())
print(str(current_time.tm_hour) + '-' + str(current_time.tm_min) + '-' + str(current_time.tm_sec))
if current_time.tm_hour == run_time_h and current_time.tm_min == run_time_m:
break

一些操作的解释

  1. selenium库的使用可以查一下其他博主的介绍
  2. 关于元素的定位,一般我习惯于使用通过ID和XPATH来定位(XPATH 就是 "//*[@xx='abc']/li[1]/")这个也可以看看如何去使用。
  3. ddddocr库是看图片的编码来转换成文本

此代码可能出现的问题(完成度不是很高):

​ 因为我本身不是曲师大的校友所以说我没有进行一个完全的操作,对系统的认识也不是很充分这样写出来的代码当然也完成度也不能说是很高的,下面我就大概说一下这个代码可能产生的问题。

  1. 如果系统崩溃(加载不出页面……),程序应该是不能执行命令。
  2. 最后的选课部分只进行了一个点击,并没有确认,因此实际上这段代码是无法完成选课的!
  3. 可能图片识别有一定的误差,导致不能登陆成功(ddddocr库可能出的问题)
  4. 操作过快以至于元素没有加载出来,这一点我通过time.sleep()函数来进行了一个优化,每次选课间隙也添加一个3秒的一个等待。
import time
import ddddocr
from selenium import webdriver
from selenium.webdriver.common.by import By class CClassSelect:
def __init__(self, i_account, i_password, i_course):
self.user_account = i_account
self.user_password = i_password
self.user_course = i_course
self.login_url = 'http://202.194.188.38/'
self.account_url = 'http://202.194.188.38/jsxsd/framework/xsMain.jsp'
self.driver = webdriver.Chrome()
self.driver.maximize_window() def LoginAccount(self):
self.driver.get(self.login_url)
self.driver.find_element(By.ID, 'userAccount').send_keys(self.user_account)
self.driver.find_element(By.ID, 'userPassword').send_keys(self.user_password)
identify_code = self.Ocr()
self.driver.find_element(By.ID, 'RANDOMCODE').send_keys(identify_code)
self.driver.find_element(By.XPATH, "//*[@class='btn btn-primary login_btn']").click() def LoginClassSelect(self):
time.sleep(1)
self.driver.find_element(By.XPATH, "//*[@id='onesidebar']/div/ul/li[3]/span").click()
self.driver.find_element(By.XPATH, "//*[@class='sidebar-menu']/li[7]/a").click()
time.sleep(1)
self.driver.find_element(By.XPATH, "//*[@class='treeview-menu menu-open']/li[1]/a").click()
self.driver.switch_to.frame("Frame1") # !!
"""
iframe问题,卡了一段时间这个地方,可以百度到是不在一个frame的原因
定位不到这个‘进入选课’元素,
"""
self.driver.find_element(By.ID, "jrxk").click()
self.driver.find_element(By.XPATH, "//*[@class='Nsb_pw']/div/center/input[1]").click() def ClassSelect(self):
new_window = self.driver.window_handles[-1]
self.driver.switch_to.window(new_window)
"""
切换到新的窗口
"""
self.driver.find_element(By.XPATH, "//*[@id='topmenu']/li[4]/a").click()
self.driver.switch_to.frame("mainFrame") # 换frame """
选课代码 ↓
"""
for my_course in self.user_course:
self.driver.find_element(By.ID, 'kcxx').send_keys(my_course[1]) # 通过课程信息查找
self.driver.find_element(By.ID, 'skls').send_keys(my_course[2]) # 通过上课老师查找
self.driver.find_element(By.XPATH, f"//*[@id='skxq']/option[{my_course[3]+1}]").click()
# 选择星期(一 - 2, 二 - 3, 三 - 4, 四 - 5, 五 - 6)
self.driver.find_element(By.XPATH, "html/body/div[3]/input[6]").click()
self.driver.find_element(By.ID, 'kcxx').clear() # 清空课程查询框
self.driver.find_element(By.ID, 'skls').clear() # 清空上课老师框
time.sleep(1)
"""
没刷新出来加一个等待1秒
"""
course_remain = self.driver.find_element(By.XPATH, "//*[@class='odd']/td[9]").text # 查看课余量
if int(course_remain) > 0:
self.driver.find_element(By.XPATH, "//*[@class='odd']/td[11]/div/a").click()
"""
这里缺少了一部分确定的代码
"""
else:
print(f'{my_course[1]}选课失败')
time.sleep(3) # 3秒的暂停 def Ocr(self):
ocr = ddddocr.DdddOcr()
img = self.driver.find_element(By.ID, 'SafeCodeImg') # 定位到验证码图片
img.screenshot('code.png') # 给验证码元素截图并保存
with open('code.png', 'rb') as f:
img_bytes = f.read() # 读取图片编码
return ocr.classification(img_bytes) # 返回图片中的验证码 def Run(self):
self.LoginAccount()
self.LoginClassSelect()
self.ClassSelect()
self.driver.quit() if __name__ == '__main__':
# 定时运行
run_time_h = 9 # 定时小时
run_time_m = 0 # 定时分钟
while True:
current_time = time.localtime(time.time())
print(str(current_time.tm_hour) + '-' + str(current_time.tm_min) + '-' + str(current_time.tm_sec))
if current_time.tm_hour == run_time_h and current_time.tm_min == run_time_m:
break # 主程序
input_account = "12345" # 用户账号
input_password = "abcde" # 用户密码
input_course = [['12345', '张三', 3], ['54321', '李四', 5]] # 要选的课(ID,上课老师,星期)
app = CClassSelect(input_account, input_password, input_course)
app.Run()

自动化选课(Python + selenium的更多相关文章

  1. web自动化 基于python+Selenium+PHP+Ftp实现的轻量级web自动化测试框架

    基于python+Selenium+PHP+Ftp实现的轻量级web自动化测试框架   by:授客 QQ:1033553122     博客:http://blog.sina.com.cn/ishou ...

  2. 一次完整的自动化登录测试-基于python+selenium进行cnblog的自动化登录测试

    Web登录测试是很常见的测试!手动测试大家再熟悉不过了,那如何进行自动化登录测试呢!本文作者就用python+selenium结合unittest单元测试框架来进行一次简单但比较完整的cnblog自动 ...

  3. python selenium自动化(二)自动化注册流程

    需求:使用python selenium来自动测试一个网站注册的流程. 假设这个网站的注册流程分为三步,需要提供比较多的信息: 在这个流程里面,需要用户填入信息.在下拉菜单中选择.选择单选的radio ...

  4. 使用python selenium进行自动化functional test

    Why Automation Testing 现在似乎大家都一致认同一个项目应该有足够多的测试来保证功能的正常运作,而且这些此处的‘测试’特指自动化测试:并且大多数人会认为如果还有哪个项目依然采用人工 ...

  5. WEB自动化(Python+selenium)的API

    在做Web自动化过程中,汇总了Python+selenium的API相关方法,给公司里的同事做了第二次培训,分享给大家                                         ...

  6. 【转】基于Selenium的web自动化框架(python)

    1 什么是selenium Selenium 是一个基于浏览器的自动化工具,它提供了一种跨平台.跨浏览器的端到端的web自动化解决方案.Selenium主要包括三部分:Selenium IDE.Sel ...

  7. python selenium自动化点击页面链接测试

    python selenium自动化点击页面链接测试 需求:现在有一个网站的页面,我希望用python自动化的测试点击这个页面上所有的在本窗口跳转,并且是本站内的链接,前往到链接页面之后在通过后退返回 ...

  8. 一次简单完整的自动化登录测试-基于python+selenium进行cnblog的自动化登录测试

    Web登录测试是很常见的测试,手动测试大家再熟悉不过了,那如何进行自动化登录测试呢!本文就基于python+selenium结合unittest单元测试框架来进行一次简单但比较完整的cnblog自动化 ...

  9. python+selenium自动化登录dnf11周年活动界面领取奖励登录部分采坑总结[1]

    背景: Dnf的周年庆活动之一,游戏在6月22日 06:00~6月23日 06:00之间登陆过游戏后可以于6月25日 16:00~7月04日 06:00领取奖励 目标:连续四天自动运行脚本,自动领取所 ...

随机推荐

  1. SQL Server 2017 各版本之间的差异

    SQL Server 2017的亮点 您选择的语言和平台 使用您选择的语言在本地和云中(现在在 Windows.Linux 和 Docker 容器上)构建现代应用程序. 行业领先的性能 充分利用任务关 ...

  2. 5. Docker compose

    把上图添加路径后,改成下图: 上图之后需要source /etc/profile #此命令重新加载环境变量文件. 在任意目录下输入docker-compose测试下,docker-compose是否安 ...

  3. conda命令的使用,环境安装,创建环境以Anaconda为例

    Anaconda用命令conda创建环境: 安装Anaconda后,用Conda –version查看conda的版本号: Conda create -n name python = x.xx Con ...

  4. 总结 到 GDOI 2021 这个阶段

    截止本蒟蒻第一次体验省选(虽然是普及组) 本蒟蒻已经有了许多收获,却也有很多不足 优点 对一些学过的知识掌握还行 没了 缺点 会却做不出来 有一些题不难,却想不到正解 如 Day2 T1 ,就是移一下 ...

  5. python logging模块使用方法

    # -*- coding: utf-8 -*- # @ModuleName: logger # @Time: 2022/6/10 11:48 # @Author : Free-A # @Descrip ...

  6. 分享自己平时使用的socket多客户端通信的代码技术点和软件使用

    前言 说到linux下多进程通信,有好几种,之前也在喵哥的公众号回复过,这里再拿出来,重新写一遍:多进程通信有管道,而管道分为匿名和命名管道 ,后者比前者优势在于可以进行无亲缘进程通信:此外信号也是进 ...

  7. CF487E Tourists 题解

    题目链接 思路分析 看到这道题首先想到的此题的树上版本.(不就是树链剖分的板子题么?) 但是此题是图上的两点间的走法,自然要想到是圆方树. 我们先无脑构建出圆方树. 我们先猜测:设后加入的节点权值为 ...

  8. CentOS7使用LVM缩减/home空间,扩大/空间

    CentOS7使用LVM缩减/home空间,扩大/空间方法:把/home里的内容备份,然后将/home文件系统所在的逻辑卷删除,扩大/文件系统.新建/home,恢复/home的原内容1.查看默认分区[ ...

  9. Qucs初步使用指南(不是multism)

    众所周知,Multism是一款强大的电路仿真软件,学习电子电路的同学都会接触到. 但是,这软件不支持Linux.(这就很魂淡了啊) 我的主力机是Linux,不能进行电路仿真成了学习的最大障碍. 使用w ...

  10. 如何优化API?8个实用技巧!【eolink翻译】

    使用 API 可以让公司利用现代连接的力量来帮助他们扩大全球影响力.传输数据和改进集成.由于 API 使企业能够简化流程并增强可用性,所以企业会使用一些优化策略,不断优化流程,比如接下来要说到的8个技 ...