首页 > 自考资讯 > 自考知识

成都信息工程大学四六级成绩分析,从数据采集到数据分析

2024-08-22

所有数据均合法且公开。详情请见文章末尾。

1.数据的获取

为了分析4级和6级数据,我们首先需要获取数据。我校教务处网站已提供按学生姓名查询四、六级成绩的功能。

成都信息工程大学四六级成绩分析,从数据采集到数据分析

我们只需要写一个简单的脚本来遍历查询学生的姓名。为了获取学生姓名,我们还可以通过教务处网站上的班级学生名单获取全校学生名单(点此跳转,需登录)。

接下来我们来说一下如何编写脚本来获取4级和6级数据。

打开前面提到的教务处网站查询四、六年级的成绩,可以看到我们只需要输入名字就可以查询,而且网址没有改变,而且该网页的源代码中没有任何js提交数据的页面。因此,该页面只是简单地使用了一个HTML表单来向后端发布数据。

成都信息工程大学四六级成绩分析,从数据采集到数据分析

接下来我们使用Fiddler进行抓包(也可以使用Chrome的F12开发者模式)。为了完全模仿新用户的行为,我们打开Chrome 的隐身标签,然后访问该网站。我们可以看到,如下图:

访问期间是否发生了2 302 次重定向和2 200 次状态?唯一有用的是第一次,如上图所示。第一个302 响应为我们设置了一个名为ASPSESSIONIDCCDCSRDR 的cookie。后来通过构建帖子发现,如果cookie不正确或者没有cookie,则无法正常请求分数数据。

那么我们就有了第一步,就是先向这个页面发送GET请求,获取一个名为ASPSESSIONIDCCDCSRDR的cookie,然后后续构造的所有Post请求都需要携带这个Cookie。

在Python中,我们使用以下代码获取cookie并永久保存以供后续查询:

通过Python Rquests包的session永久保存一组Cookies。

import requests# 用于请求cookies header={ 'Host': 'jxgl.cuit.edu.cn', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'User-Agent ' : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, 如Gecko) Chrome/56.0.2924.87 Safari/537.36', '接受' : 'text/html,application/xhtml+xml,application/xml ; q=0.9,image/webp,__;q=0.8', '接受编码': 'gzip, deflate, sdch', '接受语言': 'zh-CN,en-US;q=0.8,en; q=0.6,zh;q=0.4' }## 获取必要的sessionsession=requests.Session() session.get('http://jxgl.cuit.edu.cn/Jxgl/Djks/Default.asp?Op=%B4%F3% D1%A7%D3%A2%D3%EF%B9%FA%BC%D2%C1%F9%BC%B6',headers=header)print(session.cookies.get_dict()) 接下来我们继续分析。我们继续抓包。这次我们捕获在查询页面输入学生姓名并点击查询时浏览器发送的数据包。

Fiddler抓包信息如下:

从上图可以看出,在发出post查询请求时,会包含cookie ASPSESSIONIDCCDCSRDR。另外,查看服务器响应的查询请求,响应长度为12944,这意味着正确返回了所需的信息。如果这个值太小(比如1000左右),有可能会返回其他页面,也就是我们构造post请求失败。

再看看下面的图片。下图是当我们向服务器发送查询命令时,浏览器向服务器发送的表单数据。通过分析,我们发现图中的1、2、3分别指定了查询成绩类型、学生姓名、查询考试时间。

上图的源数据如下:

func=loginOp=%B4%F3%D1%A7%D3%A2%D3%EF%B9%FA%BC%D2%C1%F9%BC%B6hdName=HdXm=%D5%C5%C1%FATheTime=20161217imageField22。 x=23imageField22.y=18 使用以下Python代码构造学生成绩查询的查询头(需要与上面提到的代码结合):

#用于请求成绩的头headerget={ 'Host': 'jxgl.cuit.edu.cn', 'Connection': 'keep-alive', 'Content-Length': '140', 'Cache-Control': ' max-age=0', 'Origin': 'http://jxgl.cuit.edu.cn', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36(KHTML,如Gecko)Chrome/56.0.2924.87 Safari/537.36'、'Content-Type': 'application/x-www-form-urlencoded'、'Accept': 'text/html,application/xhtml + xml,application/xml;q=0.9,image/webp,__;q=0.8','Referer':'http://jxgl.cuit.edu.cn/Jxgl/Djks/Default.asp?Op=%B4%F3% D1 %A7%D3%A2%D3%EF%B9%FA%BC%D2%C1%F9%BC%B6','Accept-Encoding': 'gzip, deflate','Accept-Language': 'zh- CN ,en-US;q=0.8,en;q=0.6,zh;q=0.4'}#查询使用的字段sdata={ 'func':'login', 'Op':'大学英语国家六级'.编码('GB2312'), 'hdName':'', 'HdXm':'张龙'.encode('GB2312'), 'TheTime':20161217, 'imageField22.x':18, 'imageField22.y':13 }#发送查询post请求r=session.post('http://jxgl.cuit.edu.cn/Jxgl/Djks/Default.asp', data=sdata,headers=headerget)# 对结果重新编码,这里防止中文乱码shtml=r.text.encode(r.encoding).decode('GB2312','ignore') 最后,由于教务处查询后返回的是网页,而不是易于处理的结构化数据,所以我们使用bs4匹配一次。 (当有同名学生时,会出现多个查询条件,此时我们需要通过学号来限制)

2.数据的储存

对于数据存储,由于一个学生可能多次考过大学英语四、六级,所以我使用了以下结构来存储数据:

[学号、考试号、姓名、班级、[[分数1:考试时间、类型、缺席、总分、听力、阅读、写作]、[分数2:考试时间、类型、总分、听力、阅读、写作]] ] /div 示例:[ 201305103*, #学号51006016220763*, #学生姓名及考试号,计算机(工程)131,#Class[ #成绩列表,列表['20160618', '大学英语国家四级', '', '439', '195', '109', '135'], ['20151219', '全国大学英语四级', '', '419', '134', '156', ' 129'], ['20150613', '大学英语四级', '', '383', '147', '119', '117'], ['20141220', '大学英语四级', ' ', '341', '134', '90', '117'], ['20161217', '全国大学英语六级', '', '278', '112', '128', '38' ]]

3.数据的分析

数据处理

接下来,我们进入数据分析环节。这里,为了方便追踪,我们以计算机学院2013级为例。

为什么选择2013级?

2013级目前是大四(本文写于2017年),相当于每年都有四六级成绩(如果六级通过的话)。数据量充足,易于定期总结。

从教务处下载了2013年计算机班名单,并核对了四、六年级,总共得到了约900条数据。 (计算机学院2013级学生总数刚刚超过200人)。

接下来我们需要使用下面的脚本来生成按照上一节提到的同学存储的数据结构。

import csvCET4='大学英语国家四级' CET6='大学英语国家六级'# 加载记录集Stulist=[] f=open('result.csv','r') csvr=csv.reader(f) for line in csvr: if len(line) 1: #print(line) Stulist.append(line)f.close() del Stulist[0] print('数据总数:',len(stulist))# 处理记录集sidrl=[ ] Stu46list=[] #存储4、6级各成绩的数据[学号、考号、姓名、班级、[[分数1:考试时间、类型、缺勤、总分、听力、阅读、写作] , [分数2:测试时间、类型、总分、听力、阅读、写作]] for Stu in Stulist: pid=Stu[2] #首先查找某个学生的所有分数scl=[ x for x in Stulist if x[ 2 ]==pid 和pid 不在sidrl 中] sidrl.append(pid) if len(scl)=0: if Stu[2] in sidrl: print(stu[3],'已处理,跳过;') else: print(stu [3],'没有考试记录;') else: print(scl[0][3],'有',len(scl),'考试记录;',end='\t') cet4=[ x for x in scl if x[11]==CET4 ] cet6=[ x for ',len(cet6),'times.',end='\t') stucetinfo=[stu[2],stu[1],stu [3],stu[7],[ ]] #在cet4:中插入考试的学生信息数组stucetinfo[4].append( [ exam[0],exam[11],exam[6],exam[5],exam [8],exam[9],exam[10] ] ) 用于cet6: 中的考试stucetinfo[4].append( [ exam[0],exam[11],exam[6],exam[5],exam[8 ],exam[9],exam[10] ] ) Stu46list.append( stucetinfo)print('\n---\n原始数据:',len(stulist),'Bar,独立学生数据:',len(stu46list ),'Bar')f=open('csCET46list.csv', 'w') csvw=csv.writer(f) csvw.writerows(stu46list) f.close() 通过在成绩结果上运行上述代码查询后,我们已经生成了所需的数据结构。

接下来我们分析这个结构数据。

四级分析

我们可以先分析一下计算机学院2013级学生从开始到现在每次四级考试的成绩变化曲线。也就是说,比较不同时间进行的大学英语四级考试成绩的变化。

我们只需要遍历每个学生的成绩列表,将对应时间段的四级成绩放入对应时间段的四级成绩列表中即可。为了使图表更加美观,我们删除了x轴上的学生姓名,并用数字代替。同时我们在绘制之前对图形进行了降序排序,这样变化趋势就非常直观了。

通过以下代码,可以将每个学生的4级成绩放在对应的时间段内:

# [[第一年考试,[成绩单:考试时间、类型、缺勤、总分、听力、阅读、写作]],[第二年考试,[成绩单:学生信息]],[第三年考试,[成绩list :学生信息]], ]# 其中stulist是学生存储的成绩列表,exam_season是考试时间列表CET4SCORE=[]# 在exam_season: CET4SCORE.append ( [ season , [ y for x in stulist for y in x[4] if y[0]==season and y[1]==CET4 ].copy() ] ) 接下来我们绘制图像,2013 Level 4 的变化曲线如下:

哇,这个性能变化简直是惨不忍睹。在上图中,我画了425点线(浅灰色的线)。

简单来说,从我2013年入学到现在,四级考试成绩基本上是一次比一次低。刚入学的时候,四级肯定是最高的。这是符合客观规律的。而且,开学时并不是每个人都能参加大学英语四级考试。因此,第二学期,也就是黄线,第二次参加大学英语四级考试的人数达到最高。

我们可以发现,随着四级考试次数的增加,缺席人数也随之增加(分数=0)。

从第三学期开始,基本上2013级全班都开始偷懒了,这和我们2015年现在的状态是一样的。大学前两个学期大家都很积极。基本上从第三学期开始,就有了明显的变化。这意味着人们正在变得懒惰。这从四级成绩中可以清楚地看出,从期末考试成绩的分析中也可以发现这一点(参见计算机学院2015年第三学期期末分析)。

另外一个有趣的事情是,当你到了后面,就很难达到400了。基本上大多数人都集中在300-400这个范围内。

如果我们进一步分析四级数据,可以画出每年的及格率变化曲线:

仍然使用上面使用的CET4SCORE变量,通过下面的代码,可以将通过率、最高分、最低分放到一个新的列表中:

caldata=[] #结构:[考试时间、通过率、最高分、最低分] for CET4SCORE: #[考试第一年,[成绩单] :[考试时间、类型、缺席、总分、听力、阅读、写作] ]] #通过这个循环,你会得到每次考试的通过率、最高分、最低分passrate=str(round(len([x[3] for x in cet4[1] if int(x [3])=425])/len(cet4[1])*100,2))+'%' maxs=max(cet4[1],key=lambda x:x[3])[3] rv=[x [3 ] for x in cet4[1] if x[3] !='0'] mins=min(rv) caldata.append([ cet4[0],passrate,maxs,mins ])caldata.reverse()print (caldata)并在matplotlib的帮助下,我们有以下两张图片:

下图是2013年计算机学院四级通过率变化曲线(分数高于425的人数除以参考人数)

上图充分说明了考试通过率的变化,与我上图的分析非常吻合。大家只是第一学期和第二学期认真一点。一旦大学的新鲜感褪去,每个人都开始变得懒惰。目测今年计算机学院2013级毕业生毕业时,仍有大量人员未能通过大学英语四级考试。因此,我校的奖励条件不设定425分作为最低要求是明智的选择。

下图是往年四级考试最高分和最低分的变化曲线(最低分不包括0分)

啧啧,我估计越到最后越难通过500了,因为英语好的/会考试的人第一学期和第二学期基本都过了,剩下的就在那里挣扎了英语不太好。

六级分析

我们也可以用同样的方法得到2013级的6级成绩:

可以看出,每年6级成绩的整体变化波动较大。我怀疑这与6级评分机制有关,而不是相对分数。还有,不知道是2015年12月的六级考试特别难还是什么的,为什么通过的人这么少……

同样,我们用同样的方法获取了2013年计算机学院大学英语六级的通过率。

6级还是和4级一样的情况,越往后通过率越低。原因一定和上面一样。

接下来是最高分和最低分

可以发现,6级最高分波动不大,最低分就不讨论了。我想之所以六级考试成绩波动不大,是因为大家都通过了四级考试。我解释一下,从理论上讲,在一定程度上,基本的(最基本的)英语能力还是有的,再加上解决问题的能力稍微聪明一点,你的分数就会上去。

计算机学院2013级,2014级,2015级同时期情况对比

2013年、2014年、2015年、2016年四级一级入学考试通过率对比如下:

从上图来看,分布还是比较正常的。 2015级开始招生1人(四川),很大一部分学生来自四川,所以2015级及2016级后续班的通过率非常高。 2013年和2014年之前的班级的通过率在当时看来处于正常水平。

接下来是这些年级的平均分、最高分和最低分:

上图为含0分的平均分,下图为不含0分的平均分:

令人难以置信的是,2013级的平均成绩如此之高。不知道当时高考英语四级是不是没有要求英语一定成绩。

从最高分来看,也很正常,因为2016级也录取了一个班的学生,而且高考平均分比我们2015级高,所以英语最高分高也是很正常的。然而,2014、2015、2016级的平均分都没有超过425……不过,通过率却高达69%,可见失败的人不少。

单独分析2015级的数据

以下为2015年计算机学院大学英语四、六级成绩变化情况: 简单来说,就是越往后越难通过。超过500就更难了(至少在我们学校是这样。)

(超过)

4.其它

大学真好玩,请尽快考过四六级

猜你喜欢