【Python网络爬虫实战篇】关于在青果教务网络管理系统爬取学生成绩的分析及代码展示——以郑州轻工业大学(zzuli)教务网络管理系统为例

时间:2020-7-31 作者:admin


        关于使用Python爬虫爬取教务网络管理系统的学生成绩,实际上博主在几个月前上课期间就已经写好了。记得当时是因为嫌查成绩麻烦懒得登录网页、恰好又学习了Python爬虫所以萌发了运行代码一键爬成绩的想法(我真是个小机灵鬼x懒是人类进步的根源)。接下来我先记录、分析一下当时爬取教务网络管理系统所遇到的问题,然后展示我所编写的代码。

思路分析

        由于时间间隔较长,我就不再进行详细的爬取分析了。总体上来说,郑州轻工业大学教务网络管理系统使用的是青果教务网络管理系统的模板,只要了解了青果教务网络管理系统的源代码构造后,完成网络爬虫的爬取简直是轻而易举。同样的,不管是哪所高校,只要使用了青果教务网络管理系统,依葫芦画瓢就能进行爬虫的编写。
        因为青果教务网络管理系统作为一家普遍应用于全国各大高校的教务网络管理系统,自然具备着一定的反爬虫能力。所以一般的requests模块爬取还是有一定问题的。因此,这里我们主要使用的是Selenium自动化测试,及在网络爬虫被称之为“可见即可爬”的强大模块。由于时间问题(其实是懒),我就不进行函数封装了(博主在简单的代码编写中习惯先写好代码然后再封装为函数)。接下来,我们来说说分析的主要步骤:

        1.我们想要进入教务网络管理系统页面,自首当其冲的目标是解决登录问题。那么我们打开郑州轻工业大学的教务网络管理系统官网的“开发者工具”进行分析。观察登录模块的整体源代码,可以发现我们要解决的第一个问题:Frame子页面问题。因为账号登录的整个模块是囊括在Frame子页面中,所以直接获取输入框和按钮是获取不到的。这时,我们可以通过以下代码解决:

browser.switch_to.frame('frm_login')   # 进入教务网的子Frame页面

【Python网络爬虫实战篇】关于在青果教务网络管理系统爬取学生成绩的分析及代码展示——以郑州轻工业大学(zzuli)教务网络管理系统为例
        2.进入到教务网络管理系统的Frame子页面后,我们就可以直接获取到输入框和按钮了。关于账号和密码的输入,可以通过三种方式:① 直接写死到send_keys里面输入;② input()方法手动输入;③ 通过读取文件输入。这三种方式我都试过,最后是通过读入文件输入的(下列代码演示是通过input()方法输入)。

login = input("请输入账号: ")
password = input("请输入密码: ")
browser.find_element_by_id('txt_asmcdefsddsd').send_keys(login)
browser.find_element_by_id('txt_asmcdefsddsd').send_keys(Keys.TAB)   # TAB键是制表符,输入完账号后按下TAB跳至下一行密码输入框
browser.find_element_by_id('txt_pewerwedsdfsdff').send_keys(password)
browser.find_element_by_id('txt_sdertfgsadscxcadsads').click()   # 点击验证码框使验证码显示出来

        如果需要读入文件输入,这里我使用的是txt文本。文本名称为“账号密码.txt”,文本内格式为账号和密码分别单独占一行。读取账号密码txt文本的代码如下所示:

with open('账号密码.txt', 'r') as file:   # 账号和密码分别为一行
    user = file.readlines()   # 读取账号密码到列表中
    login = user[0].strip()   # 账号
    password = user[1].strip()   # 密码

        3.验证码问题。关于验证码的问题,我使用过python的OCR库进行了灰度化和二值化,但是识别效果很不理想(毕竟验证码的目的就是为了限制爬虫,属于反爬虫的手段之一)。所以,这里我使用Python的PIL库进行页面的整体截图+精细裁剪获取验证码并自动弹出

# 验证码截图获取
image_file = "验证码.png"
screenshot = browser.save_screenshot(image_file)   # 获取屏幕截图,保存成验证码.png
wait = WebDriverWait(browser, 20)   # 显式等待最长20秒
img = wait.until(EC.presence_of_element_located((By.ID, 'txt_sdertfgsadscxcadsads')))   # 定位图片位置
time.sleep(1)
location = img.location
size = img.size
top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']
# print("验证码位置:", top, bottom, left, right)   # 366 463 236 432
im = Image.open(image_file)
im = im.crop((left+210, top+275, right+206, bottom+280))   # 有头模式验证码位置
# im = im.crop((left+130, top+125, right+90, bottom+130))   # 无头模式验证码位置
im.save(image_file)   # 保存截取后的图片
im.show(image_file)   # 显示图片

code = input("请输入识别后的验证码: ")
browser.find_element_by_id('txt_sdertfgsadscxcadsads').send_keys(code)
browser.find_element_by_id('btn_login').click()

        4.输入验证码登录成功后,这里有些细节需要注意了!因为在多次尝试后,我发现在教务网络管理系统登录时如果之前登录的cookies生命周期还未消亡,此时登录会弹出“上次登录已下线”的弹窗。这里,我们通过一个if语句判断、捕获一下可能出现的弹窗问题

# 判断是否出现弹窗,如果出现则进行捕获并点击确认
if browser.switch_to.alert:
    get_window = browser.switch_to.alert   # 捕获弹窗“您在别处的登录已下线”
    time.sleep(1)
    get_window.accept()   # 点击确认按钮
print("登录成功,现在您已到达郑州轻工业大学教务网络页面!")

        5.打开开发者工具中的Network,点击“学生成绩”下的“查看成绩”选项,观察到新增加了Stu_MyScore.aspx,这里我们点击找到Request URL。这样,我们登录后只需请求该url就能够获取网页源代码并进行成绩的爬取。
【Python网络爬虫实战篇】关于在青果教务网络管理系统爬取学生成绩的分析及代码展示——以郑州轻工业大学(zzuli)教务网络管理系统为例

        6.接下来就没什么好说的,不过就是通过Selenium进行页面元素的获取完成爬虫。唯一需要注意的是青果教务网络管理系统的学生成绩不是文字的,因此不能直接进行爬取。这里我们通过selenium的屏幕截图代码直接截图解决。

browser.save_screenshot()

代码展示

from selenium import webdriver
import time
# import requests
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select   # 下拉选择框
# import tesserocr
from PIL import Image

with open('账号密码.txt', 'r') as file:   # 账号和密码分别为一行
    user = file.readlines()   # 读取账号密码到列表中
    login = user[0].strip()   # 账号
    password = user[1].strip()   # 密码
# chrome_options = webdriver.ChromeOptions()
# chrome_options.add_argument('--headless')
# browser = webdriver.Chrome(options=chrome_options)   # 开启无头模式
browser = webdriver.Chrome()
# browser = webdriver.PhantomJS(executable_path=r'D:\PhantomJS\phantomjs-2.1.1-windows\phantomjs-2.1.1-windows\bin\PhantomJS')
browser.get('http://jiaowu.zzuli.edu.cn/jwweb/home.aspx')
time.sleep(2)   # 延时等待Frame子页面加载
browser.switch_to.frame('frm_login')   # 进入教务网的子Frame页面

# login = input("请输入账号: ")
# password = input("请输入密码: ")
browser.find_element_by_id('txt_asmcdefsddsd').send_keys(login)
browser.find_element_by_id('txt_asmcdefsddsd').send_keys(Keys.TAB)   # TAB键是制表符,输入完账号后按下TAB跳至下一行密码输入框
browser.find_element_by_id('txt_pewerwedsdfsdff').send_keys(password)
browser.find_element_by_id('txt_sdertfgsadscxcadsads').click()   # 点击验证码框使验证码显示出来

# 验证码截图获取
image_file = "验证码.png"
screenshot = browser.save_screenshot(image_file)   # 获取屏幕截图,保存成验证码.png
wait = WebDriverWait(browser, 20)   # 显式等待最长20秒
img = wait.until(EC.presence_of_element_located((By.ID, 'txt_sdertfgsadscxcadsads')))   # 定位图片位置
time.sleep(1)
location = img.location
size = img.size
top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']
# print("验证码位置:", top, bottom, left, right)   # 366 463 236 432
im = Image.open(image_file)
im = im.crop((left+210, top+275, right+206, bottom+280))   # 有头模式验证码位置
# im = im.crop((left+130, top+125, right+90, bottom+130))   # 无头模式验证码位置
im.save(image_file)   # 保存截取后的图片
im.show(image_file)   # 显示图片

print("欢迎来到郑州轻工业大学教务系统登录页面!")
code = input("请输入识别后的验证码: ")
browser.find_element_by_id('txt_sdertfgsadscxcadsads').send_keys(code)
browser.find_element_by_id('btn_login').click()
time.sleep(1)
# 判断是否出现弹窗,如果出现则进行捕获并点击确认
if browser.switch_to.alert:
    get_window = browser.switch_to.alert   # 捕获弹窗“您在别处的登录已下线”
    time.sleep(1)
    get_window.accept()   # 点击确认按钮
print("登录成功,现在您已到达郑州轻工业大学教务网络页面!")
# time.sleep(10)
# browser.close()

browser.get('http://jiaowu.zzuli.edu.cn/jwweb/xscj/Stu_MyScore.aspx')   # 学生成绩里面的ajax地址
time.sleep(2)

browser.find_element_by_id('SelXNXQ_2').click()   # 点击学期
cj = Select(browser.find_element_by_id('sel_xn'))
year = input("请输入学年:(例如2019-2020学年输入2019)\n")
cj.select_by_value(year)
xq = Select(browser.find_element_by_id('sel_xq'))
xueqi_input = input("请输入学期:(第一学期输入1/第二学期输入2)")
xueqi = str(int(xueqi_input)-1)
xq.select_by_value(xueqi)
cjlx = input("请输入成绩类型:(原始/有效)\n")
if cjlx == "原始":
    browser.find_element_by_id("ys_sj").click()   # 点击原始成绩
    browser.find_element_by_name("btn_search").click()   # 点击检索
    time.sleep(5)
    browser.save_screenshot(str(year) + "-" + str(int(year)+1) + '学年第' + xueqi_input + '学期原始成绩.png')
elif cjlx == "有效":
    browser.find_element_by_id("yx_sj").click()  # 点击有效成绩
    browser.find_element_by_name("btn_search").click()  # 点击检索
    time.sleep(5)
    browser.save_screenshot(str(year) + "-" + str(int(year) + 1) + '学年第' + xueqi_input + '学期有效成绩.png')
print("提示: 用户[" + login + "]在" + year + "-" + str(int(year)+1) + "学年第" + xueqi_input + "学期的" + cjlx + "成绩查询成功!")
browser.close()

运行效果展示

【Python网络爬虫实战篇】关于在青果教务网络管理系统爬取学生成绩的分析及代码展示——以郑州轻工业大学(zzuli)教务网络管理系统为例
【Python网络爬虫实战篇】关于在青果教务网络管理系统爬取学生成绩的分析及代码展示——以郑州轻工业大学(zzuli)教务网络管理系统为例

总结

        第一次写这篇博客时我写的很详细、写了好多字,但是没想到发布后竟然丢了一大半!心态崩了啊!这个二周目我就不写那么多了,勉强我觉得还可以。
        因为时间原因(其实是懒),我未发布无头模式的教务网络管理系统爬虫。有头模式和无头模式的爬虫区别主要就在于验证码位置和成绩截图位置的问题,因为我只是为了图个乐呵、因此对于无头模式的成绩页面打开是缩放的问题没有解决。感兴趣的小伙伴可以根据我发布的有头模式的代码进行一定的扩展延伸,修改成无头模式并添加其他功能。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。