首页 > 源码分享 > PHP > PHP模拟登录正方抓取考试成绩20161209最新版
2016
12-09

PHP模拟登录正方抓取考试成绩20161209最新版

做了山科大校外服务平台之后,很多人问我到底怎么模拟登陆正方系统来抓取课表和成绩的,其实如果你知道了浏览器是怎么和服务器交互数据的话,实现起来就很简单了。

首先简单的说下表单的原理:当我们输入学号、密码、验证码,点击提交时会产生一个post请求,服务器在接收到post请求时会检查提交上来的学号、密码、验证码是否正确,正确则登陆成功。

在之前看到很多文章说模拟登陆正方时可以不输入验证码,博主亲自试了一下,无奈博主大学的正方系统更新的快,这一漏洞已经被填补了。所以只能老老实实的输验证码了…

接下来我们步入正题:

第一步,抓包。

由于我们不需要抓太复杂的数据,这里我们就使用浏览器自带的开发者工具就可以了。打开浏览器访问学校教务处网址,按F12选择network,之后在正方里填上你的学号,密码,验证码,登录,看看这期间提交的数据。

PHP模拟登录正方抓取考试成绩20161209最新版 - 第1张  | Vnoon

我们要用到的数据只有default2.aspx和xs_main.aspx?xh=”你的学号”这两个,其余的都是修饰用的css文件。点开default2.aspx可以看到你提交的表单数据。

PHP模拟登录正方抓取考试成绩20161209最新版 - 第2张  | Vnoon

其中__VIEWSTATE是asp.net服务器的状态信息,这个状态信息经测试是一个固定值,但是以防万一,我们还是需要抓取出来。
另外default2.aspx这个页面采用了302跳转,即登录成功后会跳转到xs_main.aspx?xh=”你的学号”这个网址,这一点可能是大多数人模拟登录失败的原因。

第二步,构造登录页面。

三项信息必填,学号、密码、验证码。我们前面也说了,验证码的漏洞已经修复,验证码我们可以采取两种方式,一是验证码自动识别,大家可以参考我之前的一篇文章:正方教务系统验证码识别。二是将验证码保存到本地手动填写,由于自动识别有一定的误差,所以我们着重来说这个方法。先说说验证码怎么保存到本地。

PHP模拟登录正方抓取考试成绩20161209最新版 - 第3张  | Vnoon

验证码是由一个叫checkcode.aspx的网页生成的,要保存可以用fwrite来实现。有同鞋可能会说,直接用<img src=””填上刚才的网址不行么?理论上可以,只要你能保存下这张图片所对应的cookie,这个cookie决定了你是否能够进行后续数据抓取。

具体代码如下:

首先要新建一个cookie文件夹,并且把session打开(可以解决高并发访问时登陆失败的问题)。

session_start();
$id=session_id();
$_SESSION['id']=$id;

验证码保存:

$cookie = dirname(__FILE__) . '/cookie/'.$_SESSION['id'].'.txt'; //cookie路径  
$verify_code_url = "http://210.44.176.46/CheckCode.aspx"; //验证码地址
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $verify_code_url);
curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie);  //保存cookie
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$img = curl_exec($curl);  //执行curl
curl_close($curl);
$fp = fopen("verifyCode.jpg","w");  //文件名
fwrite($fp,$img);  //写入文件 
fclose($fp);

之后构造input表单,设置好name

第三步,后端模拟登录页面

session_start();
header("Content-type: text/html; charset=gbk");  //视学校而定,博主学校是gbk编码,php也采用的gbk编码方式
function login_post($url,$cookie,$post){
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);  //不自动输出数据,要echo才行
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);  //重要,抓取跳转后数据
	curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie); 
	curl_setopt($ch, CURLOPT_REFERER, 'http://210.44.176.46/');  //重要,302跳转需要referer,可以在Request Headers找到 
	curl_setopt($ch, CURLOPT_POSTFIELDS,$post);  //post提交数据
	$result=curl_exec($ch);
	curl_close($ch);
	return $result;
}
$_SESSION['xh']=$_POST['xh'];
$xh=$_POST['xh'];
$pw=$_POST['pw'];
$code= $_POST['code'];
$cookie = dirname(__FILE__) . '/cookie/'.$_SESSION['id'].'.txt';
$url="http://210.44.176.46/default2.aspx";  //教务处地址
$con1=login_post($url,$cookie,'');
preg_match_all('/<input type="hidden" name="__VIEWSTATE" value="([^<>]+)" \/>/', $con1, $view); //获取__VIEWSTATE字段并存到$view数组中
$post=array(
	'__VIEWSTATE'=>$view[1][0],
	'txtUserName'=>$xh,
	'TextBox2'=>$pw,
	'txtSecretCode'=>$code,
	'RadioButtonList1'=>'%D1%A7%C9%FA',  //“学生”的gbk编码
	'Button1'=>'',
	'lbLanguage'=>'',
	'hidPdrs'=>'',
	'hidsc'=>''
);
$con2=login_post($url,$cookie,http_build_query($post)); //将数组连接成字符串
echo $con2;

如果你之前代码都正确的话,可以看到如下熟悉的界面:

PHP模拟登录正方抓取考试成绩20161209最新版 - 第4张  | Vnoon

出来这个界面就说明你已经成功了一半。接下来我们去抓取考试成绩的数据。

第四步,后端成绩查询。

博主正方更新后成绩查询是这样子的:

PHP模拟登录正方抓取考试成绩20161209最新版 - 第5张  | Vnoon

老办法,抓包。

选好学年、学期,点学期成绩之前,打开开发者工具–network

PHP模拟登录正方抓取考试成绩20161209最新版 - 第6张  | Vnoon

成绩查询的url组成是http://教务处地址/xscjcx.aspx?xh=学号&xm=姓名&gnmkdm=XXX

gnmkdm是个固定值,干什么用博主也不知道。不过查询的时候可以不带这个参数,直接http://教务处地址/xscjcx.aspx?xh=学号&xm=姓名 就可以

那么问题来了,姓名从哪来?还记得刚才登录成功那个界面么,我们可以用正则表达式提取姓名。

preg_match_all('/<span id="xhxm">([^<>]+)/', $con, $xm);   //正则出的数据存到$xm数组中
$xm[1][0]=substr($xm[1][0],0,-4);  //字符串截取,获得姓名

之后查看post数据,这次多了好多。注意:再次抓取VIEWSTATE字段。

要正则的页面:http://教务处地址/xscjcx.aspx?xh=学号&xm=姓名 (点了成绩查询后的页面)

正则表达式:

$url2="http://210.44.176.46/xscjcx.aspx?xh=".$_SESSION['xh']."&xm=".$xm[1][0];
$viewstate=login_post($url2,$cookie,'');
preg_match_all('/<input type="hidden" name="__VIEWSTATE" value="([^<>]+)" \/>/', $viewstate, $vs);
$state=$vs[1][0];  //$state存放一会post的__VIEWSTATE

Post数据:

$post=array(
       '__EVENTTARGET'=>'',
       '__EVENTARGUMENT'=>'',
       '__VIEWSTATE'=>$state,
       'hidLanguage'=>'',
       'ddlXN'=>'2015-2016',  //当前学年
       'ddlXQ'=>'1',  //当前学期
       'ddl_kcxz'=>'',
       'btn_xq'=>'%D1%A7%C6%DA%B3%C9%BC%A8'  //“学期成绩”的gbk编码,视情况而定
    );

模拟登录:

$content=login_post($url2,$cookie,http_build_query($post));
echo $content;

一切正常的话就会看到下面的界面:

PHP模拟登录正方抓取考试成绩20161209最新版 - 第7张  | Vnoon

如果想去掉多余的字段,可以用正则表达式:

preg_match_all('/
<td>([^<>]+)/', $content, $cj);
for($n=0;$n<count($cj[1]);$n++){
  echo $cj[1][$n];
}

如有不明白的地方可在下方留言。以上。

最后编辑:
作者:vnoon
一个不爱学习的电气专业学生,一个不务正业的程序猿
捐 赠如果您觉得这篇文章有用处,请支持作者!鼓励作者写出更好更多的文章!

4 Responses to PHP模拟登录正方抓取考试成绩20161209最新版

  1. 李润清 says:

    我们学校把直接登录教务系统的页面给取消了,只能通过智慧校园页面登录,然后在点击教务系统,才能进去查成绩,这个怎么破呢

留下一个回复

你的email不会被公开。