#本文介绍功能1的实现,功能2、3日后更新#
1、自动化看网课
2、自动化获取各章节题目,并转发至公众号获取答案
3、根据上面获取的答案自动答题(因作用不大,拟开发)
Selenium是最广泛使用的开源Web UI(用户界面)自动化测试套件之一。它最初由杰森·哈金斯(Jason Huggins)于2004年开发,作为Thought Works的内部工具。Selenium支持跨不同浏览器,平台和编程语言的自动化。
Selenium可以轻松部署在Windows,Linux,Solaris和Macintosh等平台上。此外,它支持IOS(IOS,Windows Mobile和Android)等移动应用程序的OS(操作系统)。
Selenium通过使用特定于每种语言的驱动程序支持各种编程语言。Selenium支持的语言包括C#,Java,Perl,PHP,Python和Ruby。目前,Selenium Web驱动程序最受Python和C#欢迎。 Selenium测试脚本可以使用任何支持的编程语言进行编码,并且可以直接在大多数现代Web浏览器中运行。 Selenium支持的浏览器包括Internet Explorer,Mozilla Firefox,Google Chrome和Safari。
更多介绍->Selenium简介
使用方法->官方文档
语言:Python(不得不说小工具首选python)
浏览器:Microsoft Edge,其他浏览器也行
浏览器驱动器:自行下载对应浏览器及版本的驱动器
(参考下载地址:edge浏览器驱动 chrome浏览器驱动)
# 标*为必用的库,不标可视功能选用
from selenium import webdriver # *浏览器驱动
from time import sleep # *等待特定时间
import time # 用于控制台输出时间
from lxml import etree # *解析页面数据
import tkinter # 图形化界面
from tkinter import ttk # 下拉框
import tkinter.messagebox # 消息框
from selenium.common.exceptions import NoSuchElementException
# *用于特定异常处理
代码如下:
from selenium import webdriver
from time import sleep
import time
from lxml import etree
import tkinter
from tkinter import ttk
import tkinter.messagebox
from selenium.common.exceptions import NoSuchElementException
代码如下:
def __init__(self):
self.window = tkinter.Tk()
self.window.title("学习通自动刷课")
# 设置窗口大小
winWidth = 300
winHeight = 200
# 获取屏幕分辨率
screenWidth = self.window.winfo_screenwidth()
screenHeight = self.window.winfo_screenheight()
# 居中显示
x = int((screenWidth - winWidth) / 2)
y = int((screenHeight - winHeight) / 2)
self.window.geometry("%sx%s+%s+%s" % (winWidth, winHeight, x, y))
tkinter.Label(self.window, text="用户名:").grid(row=1, column=1)
self.username = tkinter.StringVar()
tkinter.Entry(self.window, textvariable=self.username).grid(row=1,
column=2)
tkinter.Label(self.window, text="密 码:").grid(row=2, column=1)
self.password = tkinter.StringVar()
tkinter.Entry(self.window, textvariable=self.password,
show="*").grid(row=2, column=2)
tkinter.Label(self.window, text="科目名:").grid(row=3, column=1)
self.subjestName = tkinter.StringVar()
tkinter.Entry(self.window,
textvariable=self.subjestName).grid(row=3, column=2)
tkinter.Label(self.window, text="起始课时序号:").grid(row=4, column=1)
self.courseNum = tkinter.StringVar()
tkinter.Entry(self.window, textvariable=self.courseNum).grid(row=4,
column=2)
tkinter.Label(self.window, text="是否显示浏览器:").grid(row=5, column=1)
self.view = tkinter.IntVar()
tkinter.Radiobutton(self.window, text="是", variable=self.view,
value=1).grid(row=5, column=2, sticky='W')
tkinter.Radiobutton(self.window, text="否", variable=self.view,
value=2).grid(row=5, column=2, sticky='E')
tkinter.Label(self.window, text="选择播放速度:").grid(row=6, column=1)
self.values = [1, 1.25, 1.5, 2]
self.speed = tkinter.StringVar()
ttk.Combobox(self.window, values=self.values,
textvariable=self.speed).grid(row=6, column=2)
tkinter.Label(self.window, text="是否静音播放:").grid(row=7, column=1)
self.sound = tkinter.IntVar()
tkinter.Radiobutton(self.window,
text="是",
variable=self.sound,
value=1).grid(row=7, column=2, sticky='W')
tkinter.Radiobutton(self.window,
text="否",
variable=self.sound,
value=2).grid(row=7, column=2, sticky='E')
tkinter.Button(self.window, text="开始刷课", command=self.shuake,
width=15).grid(row=8, column=2)
self.window.protocol("WM_DELETE_WINDOW",
self.close) # 点击右上角X号关闭界面时触发函数self.close
self.window.mainloop()
def close(self):
self.flag = False
tkinter.messagebox.showinfo("close", "退出刷课?")
self.window.destroy() # 关闭页面时触发
可以挂在后台完成刷课,但只适用于Microsoft Edge,未尝试谷歌浏览器的实现方法,可自行探索
代码如下:
if (self.view.get() == 2): # self.view由图形化界面传值
EDGE = {
"browserName": "MicrosoftEdge",
"version": "",
"platform": "WINDOWS",
"ms:edgeOptions": {
'extensions': [],
'args': [
'--headless',
'--disable-gpu',
'--remote-debugging-port=9222',
]
}
}
bro = webdriver.Edge(executable_path='./msedgedriver', capabilities=EDGE)
else:
bro = webdriver.Edge(executable_path='./msedgedriver')
executable_path 指的是驱动器存放地址,我存放在同一目录下,故为 './msedgedriver’
sleep(3):睡眠3秒,防止页面加载太慢而出错
代码如下:
bro.get('http://passport2.chaoxing.com/login?fid=&newversion=true&refer=http%3A%2F%2Fi.chaoxing.com')
bro.maximize_window() #最大化窗口
bro.find_element_by_id('phone').send_keys(self.username.get())
bro.find_element_by_id('pwd').send_keys(self.password.get())
bro.find_element_by_id('loginBtn').click()
sleep(3)
例如:查找“(第八期)马克思主义基本原理”只需要输入“马克思”
代码如下:
bro.switch_to.frame('frame_content')
page_text = bro.page_source
for i in range(1, 20): # 遍历课程名
i = str(i)
tree = etree.HTML(page_text)
name_list = tree.xpath(
'/html/body/div[1]/div/div/div[2]/div/div[2]/div[2]/ul/li[' +
i + ']/div[2]/h3/a/span/text()')
sleep(1)
name_string = name_list[0]
if (name_string.__contains__(self.subjestName.get())):
sbuject_xpath = '/html/body/div[1]/div/div/div[2]/div/div[2]/div[2]/ul/li[' + i + ']/div[2]/h3/a/span'
bro.find_element_by_xpath(sbuject_xpath).click()
break
else:
continue
sleep(3)
bro.switch_to.frame(‘frame_content’) 这句很重要,因为学习通的架构中用到了iframe,要获取框架里面的标签时必须切换frame,否则会报错“NoSuchElementException”
浏览器窗口句柄:当打开一个浏览器并打开了一个新标签页时,该标签页就会有一个句柄标识(句柄值)。直到你关闭了该标签页,该句柄标识(句柄值)才消失。
所以,当我们打开一个浏览器并打开了多个标签页时,关闭一个标签页不会影响其他标签页,就是因为每个标签页有了唯一的标识。
因此,当我们打开多个页面时需要切换窗口句柄
代码如下:
all_windows = bro.window_handles # 获取全部窗口句柄
bro.switch_to.window(all_windows[-1]) # 切换到最后打开的窗口
代码如下:
for j in range(1, 20): # 遍历章节
for k in range(1, 15):
try:
j = str(j)
k = str(k)
tree = etree.HTML(page_text)
course_num_list = tree.xpath(
'/html/body/div[5]/div[1]/div[2]/div[3]/div[' + j +
']/div[' + k + ']/h3/a/span[1]/text()')
course_num_string = course_num_list[0]
if (course_num_string == self.courseNum.get()):
course_xpath = '/html/body/div[5]/div[1]/div[2]/div[3]/div[' + j + ']/div[' + k + ']/h3/a/span[1]'
bro.find_element_by_xpath(course_xpath).click()
break
else:
continue
except (IndexError):
break
sleep(3)
bro.switch_to.default_content() 返回默认层,前面进入了iframe(“frame_content”)
代码如下:
bro.switch_to.default_content() # 返回默认层
i = 1
while True:
bro.switch_to.frame('iframe') # 进入iframe
try:
num = bro.find_element_by_xpath(
'//*[@id="ext-gen1043"]/div/div/p[1]/strong/span').text
except (NoSuchElementException):
num = "无法获取" # 获取章节名称
try:
iframe2 = bro.find_element_by_xpath(
'//iframe[contains(@class,"ans-insertvideo-online")][' +
str(i) + ']')
except (NoSuchElementException):
if (num == '14.3实现共产主义是历史发展的必然趋势'): # 结束章节
bro.quit()
break
else:
i = 1
bro.switch_to.default_content()
_item = bro.find_element_by_xpath(
'//*[@id="mainid"]/div[1]/div[2]')
bro.execute_script("arguments[0].click();", _item)
sleep(2)
continue
bro.switch_to.frame(iframe2)
i += 1
try:
_item = bro.find_element_by_xpath('//*[@id="video"]/button')
bro.execute_script("arguments[0].click();", _item)
except (NoSuchElementException):
print(num, "未正常播放----")
continue
sleep(1)
# 静音播放
if (self.sound.get() == 1):
bro.find_element_by_xpath(
'//*[@id="video"]/div[5]/div[6]/button').click()
# 调节倍速
if (self.speed.get() == 1):
speed = bro.find_element_by_xpath('//*[@id="video"]/div[5]/div[1]/button')
for i in range(0, 3):
speed.click()
sleep(0.5)
sleep(1)
page_text = bro.page_source
tree = etree.HTML(page_text)
sleep(1)
time_list = tree.xpath(
'//*[@id="video"]/div[5]/div[4]/span[2]/text()')
sleep(2)
time_string = time_list[0]
time_list = time_string.split(':')
if time_list[-1][0] == "0":
time_list[-1] = time_list[-1][1]
vedio_time = eval(time_list[1]) + eval(time_list[0]) * 60
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
end='----')
print(num, "---------", "时长:", vedio_time, "秒------", "(",
time_list[0], "分", time_list[1], "秒)")
if (self.speed.get() == 1):
sleep(vedio_time / 2 + 1)
else:
sleep(vedio_time + 2)
bro.switch_to.default_content()
这里的重点是找到存放视频的那个iframe,通过解析源码找到了共同点,放视频的框架都有一个class属性含有ans-insertvideo-online,因此可以通过xpath解析'//iframe[contains(@class,"ans-insertvideo-online")][' + str(i) + ']'
,由于一个章节可能不止一个视频,因此需要继续寻找直到报错‘NoSuchElementException’,此时进入下一章节。
还有一个细节是学习通把播放按钮‘隐藏’起来了(去年还没有,今年网站更新了,防了一手),因此需要通过Javascript来点击。
_item = bro.find_element_by_xpath('//*[@id="mainid"]/div[1]/div[2]')
bro.execute_script("arguments[0].click();", _item)
其余代码都一个道理,便不再赘述。
以上就是关于这个小工具的分享,代码中多处用到异常处理,这不是一个好的习惯,但在此只为实现功能,便也就无所谓了。由于这个小工具只是私用,因此代码比较简陋,仍有较多地方需要改进(懒得改了)。各位可以参考一下思路,斟酌采用,可以的话还请点个赞或留下您宝贵的意见!