一 介绍
对于新手使用正则表达式开发PHP爬虫的话,难以入手。这里使用了一个html解析类库:simple_html_dom。通过php的curl获取网页内容,传入simple_html_dom进行解析。
因为外网无法访问教务信息管理系统,外网能查询成绩的只有柳苑晨曦网站,所以本文就以柳苑晨曦成绩查询为爬虫的对象。
本文为成绩查询:https://pingxonline.com/app/transcript/ 核心抓取代码。
二 模拟登录原理
打开Fiddler 4 抓包软件,自动监听当前网络,回到柳苑晨曦成绩查询输入账号密码登录,即可抓到登录用的数据包。
图 1 数据包内容
图1了解到数据包提交方式为POST,并产生了Seesion ID。
图2 原始数据格式
图2切换到TextView选项卡,可以看到提交的数据。
图3 格式化后的数据
图3是格式化后的数据,可以分为三个字段,第一个是“userName”为学号,第二个是“password”为登录密码,第三个就是提交类型了,这个是固定的值。
分析数据包后,然后使用curl模拟POST提交登录。到这一步我门得知了,POST请求网址:http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/login/chengji,请求的方法为POST和请求所发送的数据,以及成绩显示的网页地址为:http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/chengji.html。
三 curl模拟POST
第一步:请求登录
模拟登录实际就是正常登录的时候,php会产生一个Seesionid验证用户。请求登录就是要在这个Seesionid产生了登录的记录,在Seesion生命周期结束前,可以获取到成绩数据。我们需要把每个Seesionid保存下来进行第二次的请求。
填写账号密码进行测试发现如果密码是对的,服务器不返回任何文本;但是如果填写错误的密码,则会返回登录界面的内容。
第二步:二次请求成绩网页内容
在第一步拿到Cookies文件后,我们需要将它一同发送到成绩显示的网页:http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/chengji.html。
第一、二步完整代码:
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<?php header("Content-type:text/html; charset=utf-8"); //请求链接 $link = "http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/login/chengji"; $user = "";// 学号 $password = ""; // 密码 // 使用MD5加密学号,作为Cookies文件的名字,防止泄露 $md5genetor = md5($user); // Cookies文件保存的路径 $cookie_jar = dirname(__FILE__)."/".$md5genetor.".cookie"; // cURL 请求发生所需的头部信息,这个可以照着图1抓包得到的数据包进行填写对应的值。 $header = array( "Host:online.gxut.edu.cn", "Origin:http://online.gxut.edu.cn", "Content-Type:application/x-www-form-urlencoded", "Referer:http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/login/chengji/1.html", 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like <Gecko> </Gecko>Chrome/49.0.2623.75 Safari/537.36' ); // 登录请求所需的数据,图2分析得到。 $data = "userName={$user}&password={$password}&submit='登入'"; // 请求登录,登录成功后,会在同目录产生一个Cookies文件 $ret = curl_post($header, $data, $link, $cookie_jar); //echo $ret; // 初始化和执行第二个curl请求 $ch3 = curl_init(); curl_setopt($ch3, CURLOPT_URL, "http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/chengji.html"); curl_setopt($ch3, CURLOPT_HEADER, false); curl_setopt($ch3, CURLOPT_HEADER, 0); curl_setopt($ch3, CURLOPT_RETURNTRANSFER,1); // 使用保存下来的Cookies文件去访问成绩网页 curl_setopt($ch3, CURLOPT_COOKIEFILE, $cookie_jar); curl_setopt($ch3,CURLOPT_HTTPHEADER,$header); $ret=curl_exec($ch3); // 输出获取的内容 echo $ret; /** * [curl_post 模拟POST登录] * @method curl_post * @param [Array] $header [头部的信息] * @param [String] $data [POST提交的数据] * @param [String] $url [请求的链接] * @param [String] $cookie_jar [Cookies文件保存的路径] * @return [String] [返回网页内容] */ function curl_post($header,$data,$url, $cookie_jar) { // curl 初始化 $ch2 = curl_init(); // curl_setopt() 函数将为一个 CURL 会话设置选项 $res= curl_setopt ($ch2, CURLOPT_URL,$url); curl_setopt($ch2, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt ($ch2, CURLOPT_HEADER, 0); //如果你想 PHP 去做一个正规的 HTTP POST,设置这个选项为一个非零值。这个 POST 是普通的 application/x-www-from-urlencoded 类型,多数被 HTML 表单使用。 curl_setopt($ch2, CURLOPT_POST, 1); //传递一个作为 HTTP “POST” 操作的所有数据的字符串。 curl_setopt($ch2, CURLOPT_POSTFIELDS, $data); //使用 PHP curl 获取页面内容或提交数据,有时候希望返回的内容作为变量储存,而不是直接输出。这个时候就必需设置 curl 的 CURLOPT_RETURNTRANSFER 选项为 1 或 true。 //这里我们需要将它作为变量储存 curl_setopt ($ch2, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch2,CURLOPT_HTTPHEADER,$header); //连接时把获得的 cookie 存为文件 curl_setopt($ch2, CURLOPT_COOKIEJAR, $cookie_jar); $result = curl_exec ($ch2); curl_close($ch2); return $result; } ?> |
四 解析HTML代码
在上面代码中,“echo $ret;”将获取得到的html代码输出到前端,所以直接能看到成绩。我们需要提取的是详细的成绩内容,去除不想要的部分,或者要单独获取某个学期的成绩这时候就要用到了simple_html_dom解析HTML代码。
这个类库下载使用的方法很简单,项目地址:https://github.com/samacs/simple_html_dom,项目文档:http://simplehtmldom.sourceforge.net/。
放在同目录下,在PHP引用:include_once('simple_html_dom.php');
重要函数:
- str_get_html() :用法 $html = str_get_html('<div id="hello">Hello</div><div id="world">World</div>'); // 将字符串转变为DOM结构
- // 查找所有的td标签并循环输出文本
foreach($html->find('td') as $element)
echo $element->plaintext. '<br>';
第一步:分析成绩查询网页
图4 HTML结构
图4中,在chrome开发者模式中可以找得到,所有的成绩都放在id为"biaoge"的tbody的td里面,所以代码可以写为:
1 2 3 4 5 6 7 8 9 |
// 解析html 字符串为DOM $htmlDOM = str_get_html($ret); // 查找到所有的td后,遍历每个td并输出文本内容 foreach($htmlDOM->find('#biaoge td') as $element){ if ($element->plaintext == "学号" || $element->plaintext == "姓名" || $element->plaintext == "学期" || $element->plaintext == "课程名称" ||$element->plaintext == "类别" ||$element->plaintext == "学分" ||$element->plaintext == "平时" ||$element->plaintext == "期中" ||$element->plaintext == "期末" ||$element->plaintext == "总评成绩" ||$element->plaintext == "绩点"||$element->plaintext == "考试性质" ||$element->plaintext == "课程代码" ||$element->plaintext == "学时") { continue; } echo $element->plaintext. '<br>'; } |
图5 输出结果
到这里就成功的把成绩提取出来了,这时候可以作为数据储存在数据库了。
五 本篇文章完整代码
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
<?php header("Content-type:text/html; charset=utf-8"); include_once('simple_html_dom.php'); //请求链接 $link = "http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/login/chengji"; $user = "";// 学号 $password = ""; // 密码 // 使用MD5加密学号,作为Cookies文件的名字,防止泄露 $md5genetor = md5($user); // Cookies文件保存的路径 $cookie_jar = dirname(__FILE__)."/".$md5genetor.".cookie"; // cURL 请求发生所需的头部信息,这个可以照着图1抓包得到的数据包进行填写对应的值。 $header = array( "Host:online.gxut.edu.cn", "Origin:http://online.gxut.edu.cn", "Content-Type:application/x-www-form-urlencoded", "Referer:http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/login/chengji/1.html", 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like <Gecko> </Gecko>Chrome/49.0.2623.75 Safari/537.36' ); // 登录请求所需的数据,图2分析得到。 $data = "userName={$user}&password={$password}&submit='登入'"; // 请求登录 $ret = curl_post($header, $data, $link, $cookie_jar); // echo $ret; $ch3 = curl_init(); curl_setopt($ch3, CURLOPT_URL, "http://online.gxut.edu.cn/cmzx/StudentsMediaCentre.php/Index/chengji.html"); curl_setopt($ch3, CURLOPT_HEADER, false); curl_setopt($ch3, CURLOPT_HEADER, 0); curl_setopt($ch3, CURLOPT_RETURNTRANSFER,1); // 使用保存下来的Cookies文件去访问成绩网页 curl_setopt($ch3, CURLOPT_COOKIEFILE, $cookie_jar); curl_setopt($ch3,CURLOPT_HTTPHEADER,$header); $ret=curl_exec($ch3); // echo $ret; // 解析html 字符串为DOM $htmlDOM = str_get_html($ret); // 查找到所有的td后,遍历每个td并输出文本内容 foreach($htmlDOM->find('#biaoge td') as $element){ // 图4可以发现本应该是th标签作为表格标题的却用td来显示,所以这里检测跳过这些多余的td if ($element->plaintext == "学号" || $element->plaintext == "姓名" || $element->plaintext == "学期" || $element->plaintext == "课程名称" ||$element->plaintext == "类别" ||$element->plaintext == "学分" ||$element->plaintext == "平时" ||$element->plaintext == "期中" ||$element->plaintext == "期末" ||$element->plaintext == "总评成绩" ||$element->plaintext == "绩点"||$element->plaintext == "考试性质" ||$element->plaintext == "课程代码" ||$element->plaintext == "学时") { continue; } echo $element->plaintext. '<br>'; } /** * [curl_post 模拟POST登录] * @method curl_post * @param [Array] $header [头部的信息] * @param [String] $data [POST提交的数据] * @param [String] $url [请求的链接] * @param [String] $cookie_jar [Cookies文件保存的路径] * @return [String] [返回网页内容] */ function curl_post($header,$data,$url, $cookie_jar) { // curl 初始化 $ch2 = curl_init(); // curl_setopt() 函数将为一个 CURL 会话设置选项 $res= curl_setopt ($ch2, CURLOPT_URL,$url); curl_setopt($ch2, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt ($ch2, CURLOPT_HEADER, 0); //如果你想 PHP 去做一个正规的 HTTP POST,设置这个选项为一个非零值。这个 POST 是普通的 application/x-www-from-urlencoded 类型,多数被 HTML 表单使用。 curl_setopt($ch2, CURLOPT_POST, 1); //传递一个作为 HTTP “POST” 操作的所有数据的字符串。 curl_setopt($ch2, CURLOPT_POSTFIELDS, $data); //使用 PHP curl 获取页面内容或提交数据,有时候希望返回的内容作为变量储存,而不是直接输出。这个时候就必需设置 curl 的 CURLOPT_RETURNTRANSFER 选项为 1 或 true。 //这里我们需要将它作为变量储存 curl_setopt ($ch2, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch2,CURLOPT_HTTPHEADER,$header); //连接时把获得的 cookie 存为文件 curl_setopt($ch2, CURLOPT_COOKIEJAR, $cookie_jar); $result = curl_exec ($ch2); curl_close($ch2); return $result; } ?> |
六 总结
总体来说,爬成绩并算太难,关键在于模拟POST登录与Cookies的处理,可以尝试使用同样的方法抓取柳苑晨曦的课程表。simple_html_dom 还有很多很强大的函数可以查阅上面给出的文档。
QQ咨询:平兄 597914752
文章评论(0)