Icebound

icebound-area

最菜爬虫-模拟登陆

假期在家里歇着,题不想刷,机器学习慕课看着无聊。。想起上学的时候给同学吹逼:“爬虫不用学,Google一下就会了”,于是决定靠着网上的资料把学校教务爬一爬。

我校教务系统:jwxt.bupt.edu.cn 外网访问有可能不稳定

第一步是登陆,一直觉得学校教务系统登陆反人类。。之前输错密码还有一些提示,现在干脆整个网页没什么变化,加个感叹号图片就完事了。

输错用户名/密码/验证码之后只有左上角一个感叹号图片提示。。

通过检测网页上这个感叹号,就可以知道我们是否登陆成功了。

然后我们用chrome浏览器的调试功能看一下登陆的逻辑。。。神奇的发现:

以明文方式传递学号和密码。。

至此,整个登陆的逻辑非常的显然了。。首先访问首页获取验证码和cookies,然后按格式向jwLoginAction.do 直接post学号和密码,就登陆成功了。

实现方面,我使用了python3.7 配合requests库来模拟登陆操作。
验证码:使用requests.get获取验证码图片,再用Image库打开,人眼看一下验证码。
cookies获取: 调用 requests.get 获取 response ,response.cookies即为获取到的cookies。
post登陆:将要发送的信息写成键-值形式,然后调用requests.post(url,data=postData,cookies=cookies) 注意带上cookies登陆。之后检查post结果,如果返回200则为正常(并不是登录成功,只是网络通信正常)
最后检查返回的网页,是否为登陆后的教务系统即可。

勉为其难的贴一下代码。。。太丑了。。(有参考github上的代码)实现了一个获取成绩并取平均数的功能。

import requests
import http.cookiejar
import io
import sys
import time
import json
import getopt
import getpass
from PIL import Image
from io import BytesIO
from requests_html import HTML
from json import encoder
url="https://jwxt.bupt.edu.cn/"
cookies = None
 
headers=\
    {
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
        "Referer":"https://jwxt.bupt.edu.cn/",
        "Connection":"keep-alive",
        "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Encoding":"gzip, deflate",
        "Accept-Language":"zh-CN,zh;q=0.8",
        "Upgrade-Insecure-Requests":"1",
        "Cache-Control":"max-age=0",
    }
def login():
    flag=1
    cnt=0
    while flag==1:
        cnt=cnt+1
        print("第",cnt,"次尝试")
        print("开始获取验证码")
        captcha_response=requests.get(url+'validateCodeAction.do?random=',cookies=cookies)
        if captcha_response.status_code!=requests.codes.ok:
            print("获取验证码失败,是否重新获取?")
            req=input("请输入1或2 1:是 2:否  ")
            if req==0:
                exit(0)
            else:
                continue
        print("获取验证码成功")
        captcha_origin=Image.open(BytesIO(captcha_response.content))
        captcha_origin.show()
        yzm=input('请输入验证码: ')
        zjh=input('请输入学号')
        mm=input('请输入密码')
        postData=\
            {
                "type":"sso",
                "zjh": zjh,
                "mm": mm,
                "v_yzm":yzm
            }
        login_response=requests.post(url+'jwLoginAction.do',data=postData,cookies=cookies)
        if login_response.status_code==requests.codes.ok:
            print("发送请求成功")
        else:
            print("由于网络问题发送请求失败,准备重试.....")
        index=login_response.text
        print(index)
        if index.find("alert.gif")!=-1:
            print("登录失败,可能是账号、密码或验证码错误")
        else:
            print("登陆成功")
            break
 
def to_float(s):
    try:
        return float(s)
    except ValueError:
        return s
 
def get_grade():
    grade_response=requests.get(url+'gradeLnAllAction.do?type=ln&oper='+'fa',cookies=cookies)
    html=HTML(html=grade_response.text)
    grade_response=requests.get(url+html.find('iframe', first=True).attrs['src'],cookies=cookies)
    table=HTML(html=grade_response.text)
    keys=[]
    for i in range(1,8):
        keys.append(table.xpath('table[last()]/tr/td/table[1]/thead/tr/th[' + str(i) + ']')[0].text)
    result=[]
    last_course=table.xpath('table[last()]/tr/td/table[1]/tr[last()]')[0]
    course_index=1
    while True:
        current_course = table.xpath('table[last()]/tr/td/table[1]/tr[' + str(course_index) + ']')[0]
        values = []
        for i in range(1, 8):
            if i == 5 or i == 7:
                values.append(to_float(current_course.xpath('tr/td[' + str(i) + ']')[0].text))
            else:
                values.append(current_course.xpath('tr/td[' + str(i) + ']')[0].text)
        result.append(zip(keys,values))
        if current_course.element==last_course.element:
            break
        course_index +=1
    return result
 
def get_avg(result):
    ans=0.0
    all_points=0.0
    for res in result:
        points=0.0
        mark=0.0
        flag=1
        for (k,v) in res:
            if k=="成绩" and isinstance(v,float)==1:
                mark=points*v
            if k=="学分":
                points=v
            if k=="课程属性" and v=="选修":
                flag=0
        if flag==1:
            ans+=mark
            all_points+=points
    return ans/all_points
 
if __name__ == '__main__':
    response=requests.get(url,headers=headers)
    cookies=response.cookies
    login()
    res=get_grade()
    print(get_avg(res))