如果没有过抢课的经历可以说是白读四年大学了,每次抢课玩的就是速度与激情,最近在学Python爬虫相关,正好可以当一次实战练习,这次使用Python标准库中的urllib与urllib2,其带有http的服务器端和客户端的应用支持。
折腾前准备Python2.7
Chrome中的开发者工具
有人说Python 2和Python 3是两门完全不同的语言,关于Python版本之争就不多说了,反正萝卜白菜各有所爱,由于关于Python3的教程太少所以我就选2.7了
Firefox的Firebug也是一个很不错的调试工具。
首先需要了解基本的Python语法,HTTP请求(post,get),详细教程请移步这里
模拟登录
首先贴出学校选课系统的主页(几年前的iframe嵌套风格,前端逼格太low没办法)
模拟登录是爬虫的里面很重要的一个环节。模拟登录一个站点时,首先要弄清网站的登录处理细节(内容,目的地…),这里通过Chrome开发者工具来抓取http数据包来分析该网站的登录流程。除此之外,我们还要分析抓到的post包的数据结构和header,要根据提交的数据结构和heander来构造自己的post数据和header。
我们要构造自己的HTTP数据包,并发送给指定url。我们通过urllib2等几个模块来实现request请求的发送和相应的接收。大部分网站登录时需要携带cookie,所以我们还必须设置cookie处理器来保证cookie。
这里我通过两种方法实现模拟登录,手动输入和cookie。
手动输入
这里先手动输入学号,密码和验证码进行模拟登陆。
点击登录,弹出输入框。
接着扒扒验证码的url。
抓取正常登录下的http请求。
可以看到浏览器以post方式向服务器提交了用户名(学号),密码和验证码。(密码都以明文方式发送而且走的是http,安全意识感人)
这里确保提交表单时和获取验证码使用了相同的cookie,否则输入验证码会一直报错。
捣鼓
首先创建一个cookie容器来存放登录验证码url时的cookie数据,通过用户的输入生成post表单数据,通过抓取的http请求构造heander,再以刚刚获取的cookiejar生成handler,最终生成一个opener对象,传递给open方法,即成功提交了post请求。
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
49
50
51
52
53
54
55
56
57import urllib2
import cookielib
import sys
import os
urne = raw_input('请输入学号: ')
pswd = raw_input('请输入密码: ')
reload(sys)
sys.setdefaultencoding("utf-8")
# 验证码地址和post地址
CaptchaUrl = "http://www.xxx.edu/ssfw/jwcaptcha.do"
PostUrl =="http://www.xxx.edu/ssfw/j_spring_ids_security_check"
# 将cookies绑定到一个opener cookie由cookielib自动管理
cookie = cookielib.CookieJar()
handler = urllib2.HTTPCookieProcessor(cookie)
opener = urllib2.build_opener(handler)
# 用openr访问验证码地址,获取cookie
picture = opener.open(CaptchaUrl).read()
# 保存验证码到本地
local = open('image.jpg', 'wb')
local.write(picture)
local.close()
# 打开保存的验证码图片 输入
os.startfile("image.jpg")
SecretCode = raw_input('请输入验证码: ')
# 根据抓包信息 构造表单
postData = {
'j_username':urne,
'j_password': pswd,
'validateCode': SecretCode,}
# 根据抓包信息 构造headers
headers = {
"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",
"Cache-Control":"max-age=0",
"Connection":"keep-alive",
"Content-Length":"59",
"Content-Type":"application/x-www-form-urlencoded",
"Upgrade-Insecure-Requests":"1",
"User-Agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36",}
# 生成post数据 构造?key1=value1&key2=value2的形式
data = urllib.urlencode(postData)
#发送请求
request = urllib2.Request(PostUrl, data, headers)
response = opener.open(request)
–
Cookie自动输入
有时还是嫌手动输入太麻烦,而且选课系统不允许一个用户同时在多个地方登录,用上一种方法模拟登陆后原有的浏览器的cookie就失效了.
直接提取已登录成功的cookie岂不是更方便,而且可以脚本和浏览器同时登录而互不影响,cookie登录也是反反爬虫的惯用做法。
为了更方便地导出chrome中的cookie可以使用EditThisCookie(好多次看成eatthiscookie)
在浏览器中点击这款插件的图标,点击向右的按钮复制到剪贴板,这里我将其保存为cookie.txt,并且放置在和脚本相同的目录下。
现在可以省掉输入用户名,密码和验证码的步骤了,一个cookie通通搞定。
1
2
3
4
5
6
7
8
9
10
11cookie_jar = cookielib.MozillaCookieJar()
cookies = open('cookies.txt').read()
for cookie in json.loads(cookies):
cookie_jar.set_cookie(cookielib.Cookie(version=0, name=cookie['name'], value=cookie['value'], port=None, port_specified=False, domain=cookie['domain'], domain_specified=False, domain_initial_dot=False, path=cookie['path'], path_specified=True, secure=cookie['secure'], expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False))
handler = urllib2.HTTPCookieProcessor(cookie_jar)
opener = urllib2.build_opener(handler)
request = urllib2.Request(PostUrl)
response = opener.open(request)
检测脚本是否登录成功登录成功
但是怎么知道脚本登录是否成功呢,一开始我的想法是:正常登录成功过后的response中的html内容中会含有姓名,班级等等一些信息,但是如果单纯正则表达式匹配,寻找DOM节点将变得相(gen)当(ben)复(bu)杂(hui),当然Python的第三方库也可以解析html,例如BeautifulSoup,功能很强大,但解析的速度很成问题,那有没有其他的办法呢。
于是我继续点击其他的按钮,同时观察http请求。
一会儿就发现规律了,每次点击触发http请求的元素时,客户端总是会发送一个get请求来获取登录状态,服务端返回一个json,在线为True,离线为False(简直像调用API一样方便)
选课请求分析
进入选课界面,写此文时正好赶上专业外通识课的选课时间,这个课是抢不完的,随便选一节课做试验。
首先看看点击选课按钮后发生了些什么请求
可以看到一共有三个http请求
可以看到第一项为获取服务器当前时间,返回一个json
第二项加了一个类似ID的参数,分析后可以知道是课程ID,获取这个ID应该可以从选课界面入手。
果不其然,对按钮审查元素过后就可以看到课程ID
由于是测试的时候是公选课,所以还带有一个xkzy(选课志愿),不加默认为第一志愿,同意response返回一个json,第一个参数判断选课是否成功,如果成功,则第二个参数为当前时间,如果选课不成功,则返回失败原因。
抢课实战