最近看了CSDN上一个专栏《Python爬虫入门教程》,其中最后一篇作者写了个例子,用爬虫计算山东大学绩点,顿时想到前一阵子搞测评的时候还得拿计算器一点点算自己的平均学分绩,也想写一个自己学校的计算学分绩的爬虫。(吐槽:这么简单的功能学校网站居然不实现以下!)

具体过程专栏作者写得很清楚,详见 [Python]网络爬虫(十):一个爬虫的诞生全过程(以山东大学绩点运算为例)。作者使用了Firefox的HTTPFox插件来进行POST和GET消息分析,并通过查看源代码来查找到模拟登陆和查询成绩时的URL(其实这里使用Firefox的FireBug插件更简单)。使用这两个插件使我这个对网站编程一窍不通的人都能搞出个模拟登陆来。

根据那篇博文,整个过程分为以下几步:

  1. 打开教务系统网站,查看登陆时发送了哪些信息,分析POST和GET信息,并在模拟登陆时使用;
  2. 进入网站后,查看如何与网站交互,同步骤一,分析这些信息,在模拟查询时使用;
  3. 使用爬虫模拟登陆和查询,得到成绩页面的html代码;
  4. 使用正则表达式提取得到的html代码中的所有成绩信息;
  5. 通过提取到的成绩信息计算平均学分绩。

具体操作细节可以参考上面那篇博文。此处不再赘述。

这里写一下我遇到的问题。上述博文中,作者登陆成绩查询系统后,直接找到成绩查询页面的URL就可以得到成绩页面的html代码,而在我们学校的教务系统中我却怎么也找不到该URL。经过N多次实验发现,我们学校查询的时候也会发送一个特定格式的POST信息,然后才会返回成绩页面,也就是说得再次模拟发送一次浏览成绩信息。感觉是由于自己对网站相关知识不了解,认为只要模仿就能取得结果,殊不知这里查询方式的不同,浪费了不少时间。

得到网页信息后,就需要写正则表达式来提取成绩信息。关于Python的正则表达式详见:[Python]网络爬虫(七):Python中的正则表达式教程。对于正则表达式不熟的人推荐一个在线测试的网站:Regex Tester ,这个网站可以测试写的正则表达式能不能正确匹配要提取的信息。

附上源代码:

 # -*- coding: utf-8 -*-
# author: Kill Console
# 功能:计算西工大研究生本学期成绩学分绩 import urllib
import urllib2
import cookielib
import re REQUIRED = '<font color=red >\xe5\xad\xa6\xe4\xbd\x8d\xe5\xbf\x85\xe4\xbf\xae\xe8\xaf\xbe</font>' # 必修
ELECTIVE = '\xe5\xad\xa6\xe4\xbd\x8d\xe9\x80\x89\xe4\xbf\xae\xe8\xaf\xbe' # 选修 class NPUSpider:
def __init__(self, stuid, pwd):
self.login_url = 'http://222.24.211.70/grsadmin/servlet/studentLogin' # 登陆url
self.query_url = 'http://222.24.211.70/grsadmin/servlet/studentMain' # 成绩查询url
self.cookie_jar = cookielib.CookieJar() # 初始化一个CookieJar来处理Cookie的信息
self.post_data_login = urllib.urlencode({'TYPE':'AUTH', 'glhj':'', 'USER':stuid, 'PASSWORD':pwd}) # POST登录数据
self.post_data_query = urllib.urlencode({'MAIN_TYPE':'', 'MAIN_SUB_ACTION':'浏览成绩', 'MAIN_NEXT_ACTION':'LL', 'MAIN_PURPOSE':'/jsp/student_JhBrow.jsp'}) # POST查询数据
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie_jar))
self.course_data = [] # 存储一门课的信息,包括课程号、课程名、学分、成绩等
self.weights = [] # 存储学分
self.scores = [] # 存储成绩
self.GPA = 0.0 def init_spider(self):
req_login = urllib2.Request(url = self.login_url, data = self.post_data_login) # 自定义一个登录请求
result_login = self.opener.open(req_login) # 访问登录页面
req_query = urllib2.Request(url = self.query_url, data = self.post_data_query) # 自定义一个查询请求
result_query = self.opener.open(req_query) # 访问成绩页面
self.process_data(result_query.read().decode('gbk').encode('utf-8')) # 由于存在中文,先解码再编码
self.calculate_GPA() def process_data(self, res_page):
self.course_data = re.findall(r'<TR>\s+<TD width=60 class=style2><div.*?>(.*?)</div>.*?<div.*?>(.*?)</div>.*?<div.*?>\s+(.*?)\s+</div>.*?<div.*?>(.*?)</div>.*?<div.*?>(.*?)</div>.*?<div.*?>(.*?)</div>.*?<div.*?>(.*?)</div>.*?</TR>', res_page, re.S)
for item in self.course_data:
self.weights.append(item[4])
self.scores.append(item[5]) def calculate_GPA(self):
sum_score = 0.0
sum_weight = 0.0
for i in range(len(self.course_data)):
if self.course_data[i][2] == REQUIRED and self.scores[i].isdigit(): # 判断该科目是否是必修且是否有分数,若是则进行计算
sum_score += float(self.weights[i]) * float(self.scores[i])
sum_weight += float(self.weights[i])
self.GPA = sum_score / sum_weight def print_GPA(self):
print u'课程代码\t\t类型\t\t学期\t\t学分\t\t成绩\t\t得分日期\t\t课程名称'
print '-'*120
for item in self.course_data:
if item[2] == REQUIRED:
rqd = '\xe5\xad\xa6\xe4\xbd\x8d\xe5\xbf\x85\xe4\xbf\xae\xe8\xaf\xbe'
print '%s\t\t%s\t%s\t\t%s\t\t%s\t\t%s\t\t%s' % (item[0], rqd, item[3], item[4], item[5], item[6], item[1])
else:
print '%s\t\t%s\t%s\t\t%s\t\t%s\t\t%s\t\t%s' % (item[0], item[2], item[3], item[4], item[5], item[6], item[1])
print
print 'The GPA is %.4f' % self.GPA def main():
stuid = raw_input('student ID: ').strip()
pwd = raw_input('pass word: ').strip() spider = NPUSpider(stuid, pwd)
spider.init_spider()
spider.print_GPA() if __name__ == '__main__':
main()

以下是一个西工大本科生学分绩计算脚本,由于测试时没有进行教学评估的查询不了,只能查询本学期成绩,所以计算的是本学期成绩学分绩(只计算必修):

 # -*- coding: utf-8 -*-
# author: Kill Console
# 功能:计算西工大本科生本学期成绩学分绩 import urllib
import urllib2
import cookielib
import re REQUIRED = '\xe5\xbf\x85\xe4\xbf\xae' # 必修
ELECTIVE = '\xe4\xbb\xbb\xe9\x80\x89' # 选修 class NPUSpider:
def __init__(self, stuid, pwd):
self.login_url = 'http://jw.nwpu.edu.cn/loginAction.do' # 登陆url
self.result_url = 'http://jw.nwpu.edu.cn/bxqcjcxAction.do' # 本学期成绩查询url
self.cookie_jar = cookielib.CookieJar() # 初始化一个CookieJar来处理Cookie的信息
self.post_data = urllib.urlencode({'zjh':stuid, 'mm':pwd}) # POST的数据
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookie_jar))
self.course_data = [] # 存储一门课的信息,包括课程号、课程名、学分、成绩等
self.weights = [] # 存储学分
self.scores = [] # 存储成绩
self.GPA = 0.0 def init_spider(self):
req = urllib2.Request(url = self.login_url, data = self.post_data) # 自定义一个请求
result = self.opener.open(req) # 访问登录页面
result = self.opener.open(self.result_url) # 访问成绩页面
self.process_data(result.read().decode('gbk').encode('utf-8'))
self.calculate_GPA() def process_data(self, res_page):
self.course_data = re.findall(r'<tr class=.*?<td.*?>\s+(.*?)\s+</td>.*?<td.*?>\s+(.*?)\s+</td>.*?<td.*?>\s+(.*?)\s+</td>.*?<td.*?>\s+(.*?)\s+</td>.*?<td.*?>\s+(.*?)\s+</td>.*?<td.*?>\s+(.*?)\s+</td>.*?<td.*?>\s+(.*?)\s+</td>.*?</tr>', res_page, re.S)
for item in self.course_data:
self.weights.append(item[4])
self.scores.append(item[6]) def calculate_GPA(self):
sum_score = 0.0
sum_weight = 0.0
for i in range(len(self.course_data)):
if self.course_data[i][5] == REQUIRED and self.scores[i].isdigit():
sum_score += float(self.weights[i]) * float(self.scores[i])
sum_weight += float(self.weights[i])
self.GPA = sum_score / sum_weight def print_GPA(self):
print 'The GPA is %.4f' % self.GPA def main():
stuid = raw_input('student ID: ').strip()
pwd = raw_input('pass word: ').strip() spider = NPUSpider(stuid, pwd)
spider.init_spider()
spider.print_GPA() if __name__ == '__main__':
main()

使用Python计算研究生学分绩(绩点)的更多相关文章

  1. Python爬虫实战七之计算大学本学期绩点

    大家好,本次为大家带来的项目是计算大学本学期绩点.首先说明的是,博主来自山东大学,有属于个人的学生成绩管理系统,需要学号密码才可以登录,不过可能广大读者没有这个学号密码,不能实际进行操作,所以最主要的 ...

  2. Python 爬虫实战3 计算大学本学期绩点

    大家好,本次为大家带来的项目是计算大学本学期绩点.首先说明的是,博主来自山东大学,有属于个人的学生成绩管理系统,需要学号密码才可以登录,不过可能广大读者没有这个学号密码,不能实际进行操作,所以最主要的 ...

  3. [转载] python 计算字符串长度

    本文转载自: http://www.sharejs.com/codes/python/4843 python 计算字符串长度,一个中文算两个字符,先转换成utf8,然后通过计算utf8的长度和len函 ...

  4. Python计算斗牛游戏的概率

    Python计算斗牛游戏的概率 过年回家,都会约上亲朋好友聚聚会,会上经常会打麻将,斗地主,斗牛.在这些游戏中,斗牛是最受欢迎的,因为可以很多人一起玩,而且没有技术含量,都是看运气(专业术语是概率). ...

  5. 利用Python计算π的值,并显示进度条

    利用Python计算π的值,并显示进度条  第一步:下载tqdm 第二步;编写代码 from math import * from tqdm import tqdm from time import ...

  6. 用Python计算幂的两种方法,非递归和递归法

    用Python计算幂的两种方法: #coding:utf-8 #计算幂的两种方法.py #1.常规方法利用函数 #不使用递归计算幂的方法 """ def power(x, ...

  7. Python计算分位数

    Python计算分位数    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/gdkyxy2013/article/details/80911514 ...

  8. python 计算校验和

    校验和是经常使用的,这里简单的列了一个针对按字节计算累加和的代码片段.其实,这种累加和的计算,将字节翻译为无符号整数和带符号整数,结果是一样的. 使用python计算校验和时记住做截断就可以了. 这里 ...

  9. 为了用python计算一个汉字的中心点,差点没绞尽脑汁活活累死

    为了用python计算一个汉字的中心点,差点没绞尽脑汁活活累死

随机推荐

  1. 数据结构和算法(What Why How)

    数据结构和算法是什么? 从广义上讲,数据结构就是指一组数据的存储结构.算法就是操作数据的一组方法. 从狭义上讲,是指某些著名的数据结构和算法,比如队列.堆.栈.二分查找.动态规划等. 数据结构和算法有 ...

  2. TypeError: cannot use a string pattern on a bytes-like object

    一劳永逸解决:TypeError: cannot use a string pattern on a bytes-like object TypeError: cannot use a string ...

  3. python基础学习笔记——列表及元组

    列表 列表的介绍  列表是python的基础数据类型之一 ,其他编程语言也有类似的数据类型. 列表的索引和切片 列表和字符串一样也拥有索引: lst = ['刘德华','周润发','周杰伦','向华强 ...

  4. js---post与get请求的区别

    request获取请求参数 最为常见的客户端传递参数方式有两种: 浏览器地址栏直接输入:一定是GET请求: 超链接:一定是GET请求: 表单:可以是GET,也可以是POST,这取决与<form& ...

  5. luogu3808 luogu3796 AC自动机(简单版) AC自动机(加强版)

    纪念一下我一晚上写了八遍AC自动机 这是加强版的: #include <iostream> #include <cstring> #include <cstdio> ...

  6. [每日App一]QQ主题要逆天!轻轻松松月入30万!

    听从吾师秦刚(微信或QQ:1111884)酋长的建议,谋哥(微信viyi88)我开始新的征程,每日更新一篇干货文章(要坚持啊!否则被酋长鄙视了). 好了,废话不多说,今天我给大家揭秘一个你从来想也木有 ...

  7. linux内核代码注释 赵炯 第三章引导启动程序

    linux内核代码注释 第三章引导启动程序 boot目录中的三个汇编代码文件   bootsect.s和setup.s采用近似intel的汇编语法,需要8086汇编器连接器as86和ld86 head ...

  8. [python学习篇][廖雪峰][1]高级特性--创建生成器 方法2 yield

    def fib(max): n, a, b = 0, 0, 1 while n < max: print b a, b = b, a + b n = n + 1 将print b 改成yield ...

  9. 【bzoj3251】树上三角形 朴素LCA+暴力

    题目描述 给定一大小为n的有点权树,每次询问一对点(u,v),问是否能在u到v的简单路径上取三个点权,以这三个权值为边长构成一个三角形.同时还支持单点修改. 输入 第一行两个整数n.q表示树的点数和操 ...

  10. 【bzoj1907】树的路径覆盖 树形dp

    题目描述 输入 输出 样例输入 1 7 1 2 2 3 2 4 4 6 5 6 6 7 样例输出 3 题解 树形dp 设f[x]表示以x为根的子树完成路径覆盖,且x为某条路径的一端(可以向上延伸)的最 ...