python 自动化——获取学生提交的作业

python 自动化——获取学生提交的作业

我这学期担任本科的操作系统助教。课程要求所有学生的作业都提交到“网络教学平台上”给助教批改,但学生数量众多,一个一个点击下载压缩包十分麻烦,还容易遗漏。我注意到网站没有批量下载解压的功能,为提高工作效率,想试着使用python来一键完成下载解压。

关于 selenium.webdriver

Selenium 通过使用 webdriver 支持市场上所有主流浏览器的自动化。 Webdriver 是一个 API 和协议,它支持多种语言,有python、java、C#、Ruby、JavaScript等。Webdriver 用于控制 web 浏览器的行为,且每个主流的浏览器都有一个特定的 WebDriver 实现,称为驱动程序。 驱动程序是负责委派给浏览器的组件,并处理与 Selenium 和浏览器之间的通信。

Selenium 框架通过一个面向用户的界面将所有这些部分连接在一起, 该界面支持不同的浏览器后端, 从而实现跨浏览器和跨平台自动化。

安装

可以使用 miniconda :

1
conda install -c conda-forge --name myenv selenium 

或者 pip :

1
pip install selenium

然后,根据你使用的浏览器,安装特定于浏览器的 WebDriver 二进制文件

浏览器 维护者 支持的版本
Chrome Chromium 所有版本
Firefox Mozilla 54及以上版本
Edge Microsoft 84及以上版本
Internet Explorer Selenium 6及以上版本
Opera Opera Chromium / Presto 10.5及以上版本
Safari Apple 10及以上版本

在模拟用户在浏览器上的选择、点击等操作方面,webdriver包提供了非常多的便利!使用方面,网上有非常多详实的手册,如果你使用python,推荐 selenium-Python 网站。

使用

不详细讨论技术细节,总的来说,有五个步骤:

  1. webdriver 初始化,获取驱动。Chrome 用 webdriver.Chrome()
  2. 打开网页链接。driver.get("http://www.python.org")
  3. 找到需要的元素。elem = driver.find_element_by_name("q")
  4. 对该元素进行操作,例如点击、提交信息等。elem.send_keys("pycon")
  5. 等待网页的响应。driver.implicitly_wait(10) 或者 WebDriverWait(driver, 10).until()

整个过程重点是要确定交互的元素,需要在编程前仔细研究网页的 HTML 源码。

自动化

获取作业

首先,获取驱动,打开网站:

1
2
3
# bb 网站 点击 统一身份认证登录
driver = webdriver.Chrome("/Pathto/chromedriver")
driver.get("https://www.bb.ustc.edu.cn/")

因为刚刚打开浏览器会比较慢,因此使用显式等待比较好。下面的代码,对应的操作就是点击“统一身份认证登录”。

1
2
3
4
5
6
7
try:
button1 = WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.LINK_TEXT, "统一身份认证登录")))
button1.click()
except:
print("bb website Error")
driver.quit()

随后就会进入科大的统一身份认证系统。同样地,先找到账号密码对应的 input 框的id,然后用 find_element_by_id 查找,将你的账号密码填入即可。

1
2
3
4
5
6
7
8
9
# 中科大身份认证
driver.implicitly_wait(10)
user = driver.find_element_by_id("username")
user.clear()
user.send_keys("xxxxxx")
password = driver.find_element_by_id("password")
password.send_keys("xxxxxx")
button2 = driver.find_element_by_id("login")
button2.click()

登录后,网站就跳入到了bb页面。要下载所有学生的作业,首先需要点击对应课程“操作系统原理与设计”,进入课程页面后,找到“评分中心”,下拉框中有“需要评分”一栏,点击后会出现已经提交作业的学生名单。

整体步骤依然不变,但为了更快找到对应元素,需要熟练地使用 webdriver 的 API 。不过我觉得,能用 id 或者 text 找,就尽量用 id 或 text 找。没有定义 id 和 text 的,才需要从 class 等入手。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 进入我的blackboard
driver.implicitly_wait(10)
osbut = driver.find_element_by_partial_link_text("操作系统")
osbut.click()

# 进入操作系统课程页面
driver.implicitly_wait(10)
butlist = driver.find_elements_by_class_name("submenuLink")
butlist[3].click()
eval_score = driver.find_element_by_link_text("评分中心")
eval_score.click()

driver.implicitly_wait(10)
need_eval = driver.find_element_by_link_text("需要评分")
need_eval.click()

程序进行到这里后,我已经可以看到提交同学的名单了。但需要更进一步,让python能帮我自动下载这些作业,才是我的目的。


先从该页面中,抠出所有提交作业的学生名。注意,find_elements* 的 API ,都是返回一个列表。以下面代码为例,返回了所有 class 为 gradeAttempt 的元素。

1
2
3
4
5
6
# 进入到评分界面 获取提交学生名单
driver.implicitly_wait(10)
students = driver.find_elements_by_class_name("gradeAttempt")
students_list = []
for button in students:
students_list.append(button.text)

然后,点击链接(表格中同学名字的链接背后就是每个人的作业界面)。需要注意的是,下载完一个同学的作业后,还需要点击返回,即 driver.back(),才能回到上面的页面。循环才能继续,否则会出现程序找不到元素的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for text in students_list:
print(text)
button = driver.find_element_by_partial_link_text(text)
button.click()

# 页面一直循环,直到 id="myDynamicElement" 出现
try:
download = WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.CLASS_NAME, "dwnldBtn")))
download.click()
except:
print(text, "Get TimeoutException")
finally:
driver.back()

解压操作

如此,我十分顺利地拿到了所有同学的作业,程序自动帮我下载到了 ~/下载 目录中。不过,我还想一步到位,除了自动下载,干脆解压的工作也让程序帮我完成了吧 :smile:。下面的代码使用了 python 中最好用也是最常用的包 osre

一般来说,作业的提交格式都是统一的,这里是“学号_姓名.zip or .rar”,那么相关的正则表达式需要如何描述呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
os.chdir("~/下载")
# 规定了 作业的提交格式 因此可以使用正则化表达
pattern = re.compile("PB[0-9]{8}(.*)\.((zip)|(rar))")
dirs = os.listdir()
for dir in dirs:
if pattern.match(dir) is not None:
# 给每位同学建立一个目录
dst = "/Path/to/HW/"+dir[:-4]
os.makedirs(dst)
# 根据后缀名使用对应的解压工具
if dir[-3:] == "zip":
os.system("unzip -d "+dst+" "+dir)
elif dir[-3:] == "rar":
os.system("unrar x "+dir+" "+dst)
print(dir, "is in HW/")
print("Deleting ", dir)
os.remove(dir)

大功告成!:happy:


python 自动化——获取学生提交的作业
https://dingfen.github.io/2021/04/05/2021-4-5-selenium/
作者
Bill Ding
发布于
2021年4月5日
更新于
2024年4月9日
许可协议