一时兴起学习了Python爬虫,于是马上想到了要爬教务的课表。当然,教务登录必须需要用获取到的Cookie去请求才能登录成功,还会有变态的验证码妨碍自动登录,但是登录过程实现还是比较简单的。
准备工作
- Python环境搭建
- 学习Python基本语法
- 了解整个过程的逻辑和需要的依赖
Python环境搭建就不多说了,Windows下安装Python,再安装PyCharm新建项目,学习一些基本的操作和Python基本语法。
这次我们用到的是Python下强大的爬虫html解析器BeautifulSoup4。
Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库。它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式。Beautiful Soup会帮你节省数小时甚至数天的工作时间。
那么首先,我们来了解一下教务的登录方式,对登录过程进行分析,以下使用Fiddler进行抓包。
访问 http://jwgl3.jmu.edu.cn/ ,我们可以看到请求的原始数据如图所示。
浏览器向 http://jwgl3.jmu.edu.cn/ 发送了一个Get请求,得到了200 OK的状态码,请求过程结束。
这个请求的Header(头部)里共有10条参数,分别是Host、Connection、Cache-Control、(*)Accept、Upgrade-Insecure-Requests、User-Agent、DNT、(*)Accept-Encoding、Accept-Language、(*)Cookie。
(红色加*号标注的为重要参数,会在后面登录请求中用到,接下来会逐条说明。)
我们看到这个请求OK后我们得到了一个名为ASP.NET_Session的Cookie,那么Cookie是什么呢?
Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。
所以本次请求有两个目的,一是为了获取登录页面,二是为了让服务器给这个请求下发一个身份,在后面的请求中要使用这个Cookie辨认身份。
接下来我们来看一下基本的登录需要什么。
可以看见,显示在外的输入共有3个,用户名、密码、验证码,也就是登录至少需要的3个值。
但是按照经验来看,页面内必定包含着隐藏值的验证,那么我们打开页面来找一下有没有其他的Input。
这里说一些html的基础知识,html拥有form标签,在进行数据提交时,在此标签内的input会将其值以POST的形式提交到对应地址。
此处有两个隐藏了的输入,分别是__LASTFOCUS和__VIEWSTATE,那么我们在之后的请求中要记得将这两个值一起传出。
观察LastFocus和ViewState的值,发现LastFocus为空,而ViewState其在每次访问页面的时候都会变化。所以之后每次请求都要拿下ViewState这个值。
接着还有一个名为BtnLoginImage的输入,这个代表的是界面上的“登录系统”按钮,也将作为一个值POST。
我们发现,浏览器还请求了一个/Common/CheckCode.aspx ,我们手动打开这个网址发现这是请求验证码的地址,并且请求是携带着Cookie去请求的,那么在之后请求验证码的过程中我们也要带着Cookie去请求。
保险起见,我们先进行一次登录过程的抓包,看看到底给服务器POST了哪些数据。
一共POST了6条数据,名字如图所示。现在我们可以开始构建请求。
我们定义主地址、用户名、密码,并且用BeautifulSoup对获取的页面进行了解析,获取VIEWSTATE的值。
# -*- coding: utf-8 -*- import urllib.request, requests, http.cookiejar, urllib, re, os from PIL import Image from bs4 import BeautifulSoup home_url = "http://jwgl3.jmu.edu.cn/Login.aspx" home_page = BeautifulSoup(urllib.request.urlopen(home_url), "html.parser", from_encoding="gb2312") get_viewstate = (home_page.find_all("input", id="__VIEWSTATE")[0])["value"] # 用户信息 username = 'xxxxxx' password = 'xxx'
接着,使用CookieJar统一管理Cookie,携带Cookie请求验证码并弹出让用户手动输入。
# 携带Cookie获取验证码并保留在CookieJar中管理 captcha_url = "http://jwgl3.jmu.edu.cn/Common/CheckCode.aspx" cookie = http.cookiejar.CookieJar() opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie), urllib.request.HTTPHandler) urllib.request.install_opener(opener) captcha = opener.open(captcha_url).read() local = open('./captcha.tif', 'wb') local.write(captcha) local.close() img = Image.open("./captcha.tif") img.show() CheckCode = input('输入验证码: ')
接下来创建需要POST的数据和POST头数据。
# 创建post数据 post_data = urllib.parse.urlencode({ '__VIEWSTATE': get_viewstate, 'TxtUserName': username, 'TxtPassword': password, 'TxtVerifCode': CheckCode, 'BtnLoginImage.x': '0', 'BtnLoginImage.y': '0' }) post_data = post_data.encode('utf-8') # 创建header数据,禁止gzip压缩,防止解码问题 headers = dict({ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Referer': 'http://jwgl3.jmu.edu.cn/Login.aspx', })
这里说明一下头数据包含的东西。
Accept代表浏览器告诉服务器我能接受什么样的数据;
Referer代表浏览器告诉服务器我是从哪个页面过来的;
接下来利用urllib.request库来构建一个完整的请求,并进行登录是否成功的判断
# 构建请求,模拟登录post login_request = urllib.request.Request(home_url, data=post_data, headers=headers) login_response = urllib.request.urlopen(login_request) login_status = login_response.read() # 解析post后页面,判断是否登录 login_page = BeautifulSoup(login_status, "html.parser") get_title = str(login_page.find_all("title")[0].get_text(strip=True)) if get_title == "集美大学综合教务管理系统": print("You Are Now Logged In !!!") flag = 1 else: print("Log In FAILED.") print("Please Retry to Log In.") flag = 0
整个登录过程到此结束。
还有一个Header参数我们没有说,那就是Accept-Encording。
这个请求是告诉服务器我是否能接受页面压缩,在这里我们在请求中并没有写,是经过测试后发现页面被gzip压缩,导致乱码无法正常解析,所以不使用这个参数。