新版方正教务系统爬虫

新版方正教务系统爬虫

技术 解释
Python 2.7
BeautifulSoup 4.X
Pycharm 2017
Firefox
Firebug
新版教务处地址 218.199.228.43

1、基本原理

GET请求

import urllib2
# 肯定要访问的页面地址
url = 'http://****?a=1&b=2'
# 构造 request 请求
request = urllib2.Request(url)
# 返回 response 响应
response = urllib2.urlopen(request)
# 打印输出结果 html页面
result = response.read()
print result

POST请求

import urllib
import urllib2

data={
    username = 'your username'
    passwd = 'your password'
}
# 肯定登陆页面 -- 根据请求肯定
url = 'http://******'
# 肯定请求头 -- request 的请求头信息请参考 http解析
headers = {
    'User-Agent':...
    'Cookie':...
    'Referer':...
}
encoded_data = urllib.urlencode(data)
request = urllib2.Request(url=url, data=encoded_data, headers=headers)
response = urllib2.urlopen(request)
result = response.read()
print result

2、模拟登陆

得到登陆url

教务处首页

根据地址栏的url进行登陆,发现并不能成功,老是返回登陆界面。使用Firebug进行抓包分析。html

抓包分析

发现真正的登录url为 http://218.197.80.13/xtgl/login_login.html
而且登陆会对cookie进行检查,选择 cookielib.CookieJar() 进行cookie操做,具体代码python

def __init__(self):
    # 经过登陆后获取 cookie
    self.cookie = ''
    # 得到模拟登录后的url
    self.res_url = ''
    # 设置登陆成功后的请求头中的Referer
    self.referer = ''
    # 定义登录url
    self.login_url = 'http://218.197.80.13/xtgl/login_login.html'
    # 定义cookie
    self.cj = cookielib.CookieJar()
    # 定义用户名和密码
    self.username = 'your username'
    self.passwd = 'your password'
    # 对登录数据进行编码
    self.encode_data = urllib.urlencode({
        # 从网页中分析获得的表单的属性 yhm 、mm, 具体代码见下
        'yhm': self.username,
        'mm': self.passwd,
    })
    self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cj))

# 模拟登陆
def login(self):
    # 构造请求, 在登陆时不检查请求头
    request = urllib2.Request(self.login_url, self.encode_data)
    # 获得反馈结果
    response = self.opener.open(request)
    # 一系列后续设置
    self.res_url = response.geturl()
    self.cookie = self.res_url[46:89].upper()
    self.referer = self.res_url.replace(';' + self.cookie, '')

因为须要对表单中的用户名和密码框进行分析,故贴上分析代码,也就是获取input框web

# 获取界面中的表单提交
def parse_form(html):
    # 使用 BeautifulSoup进行页面分析,BeautifulSoup的用法见
    # https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html
    soup = BeautifulSoup(html, 'lxml')
    data = {}
    for i in soup.find_all('input'):
        if i.get('name'):
            data[i.get('name')] = i.get('value')
    return data

如是须要获取整个页面的代码,使用 download 函数, 下面是download函数的代码json

# 第一个参数是须要得到代码的url,第二个是对服务器请求的请求次数,
def download(url, num_retries=2):
    print('Downloading:', url)
    try:
        html = urllib2.urlopen(url).read()
    except urllib2.URLError as e:
        print('Download error: ', e.reason)
        html = None
        if num_retries > 0:
            if hasattr(e, 'code') and 500 <= e.code < 600:
                # recursively retry Sxx HTTP errors
                return download(url, num_retries - 1)
    return html

最后各类参数配置好以后就直接进行登录操做就能够了,获取到登陆以后的url打印出来就行了,由于两个函数都写在类HBUE中,先建立HBUE类,再进行登陆就好。ruby

hbue = HBUE()
hbue.login()
# 打印登录后的界面
download(hbue.res_url)

因而便成功登陆到系统,而后接着进行各类操做服务器

3、得到我的信息

新版方正管理系统我的信息不能经过界面直接获取,会抓到空值,经过抓包分析以后,系统经过GET请求得到了首页的我的数据而后显示在主页上,也只有姓名和总学分两项。cookie

我的数据GET请求数据包分析session

重要内容 解释
Cookie JSESSIONID=9DF449637264440BE0110************
Referer http://218.197.80.13/xtgl/index_initMenu.html?jsdm=xs&_t=*
User-Agent Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0

其中经过刚开始登陆取得的cookie值来填充进headers中,里面的Referer中,后面的_t参数是系统对时间的一个加密参数,可是经过登陆成功后的url中能够分析出来svg

代码:函数

header = {
    'Referer':hbue.res_url,
    'User-Agent':'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0',
    'Cookie':cur_cookie
}

new_url = 'http://218.197.80.13/xtgl/index_cxYhxxIndex.html?xt=jw&_='+time+'&gnmkdmKey=index&sessionUserKey='+hbue.username
request = urllib2.Request(url=new_url, headers=header)
print 'request : ', request
response = urllib2.urlopen(request)
print response.read()

经过上面这段代码能够获取首页中的我的信息,包括姓名和总学分等。可是更加剧要的班级、学院等信息没有获取到,须要进入到我的信息界面进行获取。因而经过Firebug抓包分析,打开我的信息新界面是经过POST请求来得到界面,经过查看数据包中的POST请求,来构造data字典。

headers = {
    'Referer': hbue.referer,
    'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0',
    'Cookie': hbue.cookie,
}
data = {
    'dyym': '/xsxxxggl/xsgrxxwh_cxXsgrxx.html',
    'gnmkdm': 'N100801',
    'gnmkdmKey': 'index',
    'gnmkmc': '%E6%9F%A5%E8%AF%A2%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF',
    'layout': 'func-layout',
    'sessionUserKey': hbue.username,
}
# 对post的数据进行编码
encoded_data = urllib.urlencode(data)
url = 'http://218.197.80.13/xsxxxggl/xsgrxxwh_cxXsgrxx.html'
req = urllib2.Request(url=url, data=encoded_data, headers=headers)
response = urllib2.urlopen(req)
res = response.read()
soup = BeautifulSoup(res, 'lxml')
inf_id = {
    '学号': 'col_xh',
    '姓名': 'col_xm',
    '性别': 'col_xbm',
    '证件': 'col_zjhm',
    '专业': 'col_jg_id',
    '学院': 'col_zyh_id',
    '班级': 'col_bh_id',
}
for key, value in inf_id.items():
    print key, ' : ', soup.find(id=value).get_text().strip()

经过以上代码构造了POST请求,来访问一个新的界面,而且经过Beautiful来得到须要的各类数据

4、得到全部成绩

一样是在我的信息界面,有一个选项板中是成绩信息。若直接进行获取,则跟在首页上获取我的数据同样是空值,这涉及到对动态网页的访问,想要抓取该数据,须要了解网页是如何加载该数据的。该过程被成为逆向工程。打开该选项卡,在Firebug中查看新的请求,会发现网页经过POST提交来得到了成绩信息。查看返回结果,会发现返回的数据是json格式的,便使用Python的json模块把它解析为一个字典,具体代码以下

# 抓包分析url
url = 'http://218.197.80.13/cjcx/cjcx_cxDgXscj.html?doType=query&gnmkdmKey=N100801&sessionUserKey=' + hbue.username
# 构造请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0',
    'Cookie': hbue.cookie,
    'HOST': '218.197.80.13',
    'Referer': 'http://218.197.80.13/xsxxxggl/xsgrxxwh_cxXsgrxx.html',
}
# 经过数据包分析data,其中showCount表示一页显示多少数据
# time为1是由于刚进选项卡时提交过一次数据,代号为0,因为初始设置只会显示10条信息,因而再次进行提交,该一个页面显示90条数据,获取所有成绩
data = {
    '_search': False,
    'queryModel.currentPage': '1',
    'queryModel.showCount': '90',
    'queryModel.sortOrder': 'asc',
    'time': '1',
    'xh_id': hbue.username,
}
# 对POST提交数据进行编码
encoded_data = urllib.urlencode(data)
request = urllib2.Request(url=url, data=encoded_daheaders=headers)
response = urllib2.urlopen(request)
res = response.read()
# 经过json解析来获得字典,而后访问所有的课程名称、教师姓名、课程成绩
print '\n全部课程:'
for i in json.loads(res)['items']:
    print i['kcmc'], i['jsxm'], i['cj']

获取到了全部的课程和结果,其余信息如计算绩点之类的就很简单了

同时课程表、选课信息之类的信息获取也与之相似,经过构造请求来访问就能够了

代码详见:Github