php-curl模拟登录正方抓取考试成绩(最新版)小白教程

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

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

接下来步入正题:

第一步,抓包

打开谷歌浏览器访问学校教务处网址,按F12选择network,之后在正方里填上你的学号,密码,验证码,登录,看看这期间提交的数据。

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

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

第二步,构造登录页面

三项信息必填,学号、密码、验证码。先说说验证码怎么保存到本地。

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

具体代码如下:

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

1
2
3
4
<?php 
session_start();
$id=session_id();
$_SESSION['id']=$id;

验证码保存:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 
$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

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

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
<?php 
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;

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

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

第四步,后端成绩查询

博主正方更新后成绩查询是酱婶儿的(是不是暴露了什么T_T):

老办法,抓包。

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

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

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

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

1
2
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=姓名 (点了成绩查询后的页面)

正则表达式:

1
2
3
4
$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数据:

1
2
3
4
5
6
7
8
9
10
$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编码,视情况而定
);

模拟登录:

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

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

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

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

就是这样简单,快去试试吧!

欢迎加入QQ群:191212593 进行讨论