这是一篇针对CSCore的Selenium学习

历史背景

在北航,有这样一群人,他享有着别人所羡慕不来的东西——白金版CSCore,他可以查看自己的统计图,看到自己学习的时长与成果;他可以被计组邮箱推题,评测别人看不到的题。
我们至今未能得知白金版CSCore的来源,或许正如助教所说,这是未开发功能,只供部分人试用,又或许这是来自计组平台对于不好好做Pre的小可爱的一种Push,我们不得而知。
笔者很不幸运,便是普通版的一员,即使拿到了统计图的网址,点进去也会被骗到BiliBili的《never gonna give you up》。
然而我们是成年人了,要学会自己去获取资源,要有自己去拿到王者版CSCore的决心与努力,因此,我们就要学习WEB自动化工具——Selenium
当然,不学习也可以,它毕竟只是一个自动化软件,选择用勤劳的双手创造财富也可以,再不济,笔者也为大家提供了亡灵版CSCore,点击直接使用即可~
白金版CSCore

Selenium安装

Selenium是Python的一个工具包,所以我们需要先有Python,这里笔者推荐在安装解释器时直接使用Anaconda,其为Python的集成包,集成了Python及Python主流的工具包(库)多达1500个,让你可以开始你的Python编程。
如果你安装了Anaconda,那么大概率Anaconda中是有Selenium的,直接使用即可。
如果你只是安装了Python,那么大概率你是没有Selenium的,这时候需要在控制台输入以下指令:

1
pip install selenium

如果报错,请检查Python安装的正确性。

浏览器驱动安装

既然Selenium是WEB自动化工具,那么我们自然需要给浏览器安装驱动程序让Python可以驱动浏览器干活,这里针对不同浏览器我们要安装不同的驱动,针对不同浏览器版本也要选择安装不同的版本。
Firefox浏览器驱动:geckodriver
Chrome浏览器驱动:chromedriver
Edge浏览器驱动:MicrosoftWebDriver
Microsoft Edge举例,点击设置中的关于Microsoft Edge,我们可以看到Microsoft Edge的版本号:
Edge版本号
发现开头的是108,所以我们去官网选择开头为108的驱动进行下载,后面不一样问题不大。
Edge驱动

正式编写针对于CSCore的代码

CSCore网址分析

以上面的那道题为例,我们贴出网址如下:

1
http://cscore.buaa.edu.cn/#/problem?ProblemId=336&PieId=896

分析看出,如果用小区做例子的话,PieId就是居民楼,ProblemId就是每一住户,我们遍历搜索每一居民楼,每一住户,问问家里是否有人(题目),便可以得到所有的隐藏题目,直升王者版CSCore,问题来了:
0.Selenium让我望而却步,我不能接受
1.手动改网址需要1000x1000=10^6次,我们不能接受
2.肉眼观察每一户是否有人住(有题),我们不能接受

针对CSCore编写Selenium

Solution Zero

万事开头难,其实Selenium归根结底只是Python的一个库函数,学会则极其简单。
我们首先进行初步的设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
edge_options = Options()
edge_options.add_argument('--headless') # 使用无头模式
edge_options.add_argument('--disable-gpu') # 禁用GPU,防止无头模式出现莫名的BUG
edge_options.add_argument('window-size=1920x1080') #设置为电脑显示分辨率大小
edge_options.add_argument('--start-maximized') #全屏展开浏览器
#以上都是对于Edge的设置,若用其他浏览器同理。
# 开启开发者模式
edge_options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 禁用启用Blink运行时的功能
edge_options.add_argument('--disable-blink-features=AutomationControlled')
# 打开Edge
f = open("exercise.txt", "w")
driver = webdriver.Edge(options=edge_options)
driver.maximize_window() # 窗口最大化
driver.implicitly_wait(10) # 隐式等待10s查询元素
#以上是操作Edge驱动的基本过程

强调: 无头模式运行时不会将浏览器显示出来,所以如果你想观看自动化过程,请注释掉使用无头模式这条语句。

Solution First

1
2
3
4
5
6
7
8
9
10
11
12
for i in range(1000):
web = "http://cscore.buaa.edu.cn/#/problem?ProblemId=" + str(i) + "&PieId=896"
driver.get("http://cscore.buaa.edu.cn/#/problem?ProblemId=" + str(i) + "&PieId=896")
if i == 0:
driver.find_element(By.XPATH,
"/html/body/div/div/div[1]/div/main/div/div[1]/div/div/div/div[1]/form/div[1]/div/div[1]/div[1]/input").send_keys(
"xxx") # 输入账号
driver.find_element(By.XPATH,
"/html/body/div/div/div[1]/div/main/div/div[1]/div/div/div/div[1]/form/div[2]/div/div[1]/div[1]/input").send_keys(
"XXXX") # 输入密码
driver.find_element(By.XPATH,
'/html/body/div/div/div[1]/div/main/div/div[1]/div/div/div/div[3]/button/span').click()

看不懂没关系,我们慢慢解释:
第一行是for循环标准语句,我们需要敲响1000户的们,所以范围是1000.
第二行是定义了个变量用来存放网址,用于后面搜到题我们往文档里写入网址。
第三行是Selenium功能,利用驱动打开该网址,中间str(i)对应户,我们遍历查找896号居民楼。
第四行到第十二行是一个特判:第一次进入CSCore,我们自然会需要登陆,这时候利用Selenium功能,捕捉元素(find_element)捕捉到登陆框,利用Selenium功能(send_keys)将“xxx”自动输入进去。之后利用Selenium功能,模拟点击(click)点击登陆,进入计组平台。之后便是天高任鸟飞,海阔凭鱼跃了

TIPS:Selenium有很多定位元素的方法:

1
2
3
4
5
6
7
8
find_element_by_id()
find_element_by_name()
find_element_by_class_name()
find_element_by_tag_name()
find_element_by_link_text()
find_element_by_partial_link_text()
find_element_by_xpath()
find_element_by_css_selector()

我们采用最简单准确度高的操作,Xpath定位,毕竟我们是来学计组的,够用就行。

Xpath定位法

打开浏览器,F12进入开发者模式,出现如图所示画面,选择元素。
XPath定位1
之后我们将鼠标移到每一行上面,会发现该行元素作用的范围将会以蓝色显示出来。
XPath定位2
如图,右侧浅蓝色为我鼠标停留位置,左侧为定位到的元素,我们以CSCore的密码为例。
之后我们点击右键,按照下图所示复制出完整的XPath路径,一定要完整!否则定位大概率失效!
XPath定位3

Solution Second

之后我们解决第二个问题:自动判断,那么我们如何才能实现自动判断?道理也很简单,我们只需要找出没有搜到题和搜到题的不同点即可,搜到题,会出现题目,没有搜到题,则是空白界面,所以我们只需要定位一个题目任意的元素,每次判断该元素是否出现,即可实现这个功能,代码如下:

1
2
3
4
5
6
7
8
try:
info = driver.find_element(By.XPATH, '/html/body/div/div[1]/div[1]/div/main/div/div/div/div[1]/h2').text
except:
None
else:
if info:
f.write(web) #找到就将Web里的网址写入文件中
f.write("\n") #换行

TIPS:
如果我们只想搜编程题,则考虑搜索编程题特有的提交题目那个框。
如果我们全都要,那么考虑搜索题目这个元素即可。

结语

结果展示

至此,我们完成了Selenium针对计组的学习,之后我们想实现其他的功能按照上面类似操作即可,不记得对应的语句就去百度一下。
如果你真的这样做了,那么恭喜你的P0-P2将一帆风顺,因为你做到了许许多多的往年考题,还有自动评测。
恭喜获得王者版CSCore

完整源代码放送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  
import selenium
from selenium import webdriver
from selenium.webdriver.edge.options import Options
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
import time

edge_options = Options()
# 使用无头模式
edge_options.add_argument('--headless')
# 禁用GPU,防止无头模式出现莫名的BUG
edge_options.add_argument('--disable-gpu')
edge_options.add_argument('window-size=1920x1080')
edge_options.add_argument('--start-maximized')

# 开启开发者模式
edge_options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 禁用启用Blink运行时的功能
edge_options.add_argument('--disable-blink-features=AutomationControlled')
# 打开Edge
f = open("exercise.txt", "w")
driver = webdriver.Edge(options=edge_options)
driver.maximize_window() # 窗口最大化
driver.implicitly_wait(10) # 隐式等待10s查询元素
for i in range(1000):
web = "http://cscore.buaa.edu.cn/#/problem?ProblemId=" + str(i) + "&PieId=896"
driver.get("http://cscore.buaa.edu.cn/#/problem?ProblemId=" + str(i) + "&PieId=896")
if i == 0:
driver.find_element(By.XPATH,
"/html/body/div/div/div[1]/div/main/div/div[1]/div/div/div/div[1]/form/div[1]/div/div[1]/div[1]/input").send_keys(
"xxx") # 输入账号
driver.find_element(By.XPATH,
"/html/body/div/div/div[1]/div/main/div/div[1]/div/div/div/div[1]/form/div[2]/div/div[1]/div[1]/input").send_keys(
"XXXX") # 输入密码
driver.find_element(By.XPATH,
'/html/body/div/div/div[1]/div/main/div/div[1]/div/div/div/div[3]/button/span').click()
try:
info = driver.find_element(By.XPATH, '/html/body/div/div[1]/div[1]/div/main/div/div/div/div[1]/h2').text
except:
None
else:
if info:
f.write(web)
f.write("\n")
f.close()
print("Done")