之前想要写一下关于爬数据的文章的,发现时间有点急迫。所以今天在期末考试之前写完跟大家分享一下的我的心得,先上之前的图。
文章图片
今天我也以正方教务体统抓取成绩的例子来给大家讲解,第一次写博客,可能会写的不会,还请大家谅解,不过保证大家看的懂,请耐心看完。
好了,下面开始!!!
第一步:登陆你们的教务,这是我们学习的教务的主页http://jwxt.jit.edu.cn/,进去之后,打开httpWatch,至于没用过的话,自己去研究下,因为里面有将我们需要的Post参数和Post地址都归纳出来的,很好用。如图:
文章图片
点击左边的红点Record进行录制,然后点击登录按钮。 进去之后你会看见HttpWatch刷刷的,出来一大片数据,当然我们的从当中挑选出有用的数据。
文章图片
如图,我们打开上栏中的第一列标签,就是横线的地方。首先我们来观看下数据,请求方法:post,状态值:302,请求的URL:http://jwxt.jit.edu.cn/default2.aspx。 然后,我们来观察下面大圈圈中的数据,这里是Post所带的参数,里面有__ViewStat(我也不知道是什么,不过一起带着Post就好了,这里有个情况,我等会在下面讲,先用绿色标记),Button的值(乱码),lbLanguage(不管,先记下),RadioButtonList1(单选按钮:学生...),TextBox1:账号,TextBox2:密码。Ok....... 下面我们来看一下Button和lbLanguage和RadioButtonList1具体的值,下面的标签打开Stream,如图:
文章图片
看,里面有一些不认识的参数的值。Button="" ,lbLanguage="",RadioButtonList1="%D1%A7%C9%FA",哦了。 大家也可以看看其他标签下的内容,比如Cookies(比较重要),Content呀什么的。。 那么在代码里怎么体现呢,我们先看Post模拟登陆的这一步。
//第一个URL,等着为后面服务
public static final String login_url = "http://jwxt.jit.edu.cn";
//第一个Post模拟登陆的URL
public static final String login_url2 = "http://jwxt.jit.edu.cn/default2.aspx";
HttpPost httpPost = new HttpPost(login_url2);
//建立一个Post请求,第一步的方法是Post方法嘛
//禁止重定向,由于刚刚Post的状态值是重定向,所以我们要去禁止它,不然网页会乱飞。
httpPost.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
//设置头部信息(头部信息在刚刚的Httpwatch下面Headers标签会有,不过我感觉写多跟写少没多大区别,只是多写没有坏处吧。)
httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3;
WOW64;
Trident/7.0;
rv:11.0) like Gecko");
httpPost.setHeader("Content-Type","application/x-www-form-urlencoded");
//第一种模拟登陆传值
List params = new ArrayList();
//将刚刚获取到的值添加到List的中
params.add(new BasicNameValuePair("__VIEWSTATE","dDwtMTIwMTU3OTE3Nzs7PsvpgBGP9UryEzGkfCRBEu734TJ/"));
//params.add(new BasicNameValuePair("__VIEWSTATE", "/wEPDwUJNDcyMzA1MjkxZGRTx3lVi2lf6h+y/PVVH1qMZzouJg=="));
//params.add(new BasicNameValuePair("__EVENTVALIDATION", "/wEWCwLkl9v4DwLs0bLrBgLs0fbZDAK/wuqQDgKAqenNDQLN7c0VAuaMg+INAveMotMNAoznisYGArursYYIAt+RzN8IIdJ+D2D5xaddz6rv7AABSyHhO14="));
params.add(new BasicNameValuePair("TextBox1", "1205107009"));
// 账号
params.add(new BasicNameValuePair("TextBox2", "*********"));
// 密码(密码先保密吧。需要的话私聊)
params.add(new BasicNameValuePair("RadioButtonList1", "%D1%A7%C9%FA"));
// 学生
params.add(new BasicNameValuePair("Button1", ""));
params.add(new BasicNameValuePair("lbLanguage", ""));
【安卓HttpClient+Jsoup+Httpwatch模拟登陆正方教务获取信息】这样的话就把要模拟登陆的所有Post参数全部放到这个List当中了。大家看看在params.add值的时候有没有注意到我把其中的两行给注释掉了?还记得刚刚我记的绿色的标记不?这里我给大家解释一下,由于我们学校教务返回参数的时候会两种不同的情况,就是有时候是只但会一个__VIEWSTATE的值,有时候返回__VIEWSTATE和__EVENTVALIDATION这两个值,而且两个__VIEWSTATE还不一样,这是我用HttpWatch观察到的,不知道你们学校的教务会不会出现这中情况,你们自己注意一下。这里我就直接使用第一种只传递__VIEWSTATE的值给大家做例子,另外一种情况我都注释在第一种情况的下行代码。ps:有的学校没有这种情况,而且居然不要验证码直接就能进入教务主页,比如像南邮。好,贴完刚刚将值加入List的代码,现在我们开始进行模拟登陆了。
try {
// 传递参数的时候注意编码使用,否则乱码
httpPost.setEntity(new UrlEncodedFormEntity(params, "GBK"));
//响应请求
HttpResponse response = client.execute(httpPost);
//获取响应状态码
int Status = response.getStatusLine().getStatusCode();
//302表示重定向状态
if(Status == 302||Status == 301){
//获取响应的cookie值
cookie = response.getFirstHeader("Set-Cookie").getValue();
//获取头部信息中Location的值
location = response.getFirstHeader("Location").getValue();
}
}
这一步是设置下Post值的编码方式,然后进行响应请求获取返回的信息。响应的状态码为302,刚刚已经在HttpWatch中看到了。那个这个时候重定向到哪里了呢,这个时候我们发现在Post下面的Get方式,通过Get方式访问的URL是http://jwxt.jit.edu.cn/xs_main.aspx?xh=1205107009,是一个带着查询字符串的URL,那么这个URL怎么获取呢。 这里我来解释一下,重定向后的信息都储存在头部信息中。如下图:
文章图片
不然看出location的值就是主页地址的结尾部分。 这个时候的mianUrl = login_url + location即为http://jwxt.jit.edu.cn/xs_main.aspx?xh=1205107009,然后下一步是Get请求,状态码是200。这样就好做多了,不是吗?
HttpGet get = new HttpGet(mianUrl);
//关键点,这里有个关键点就是设置头部信息中的Referer和cookie值,cookie值大家都知道,模拟登陆的时候必须带着cookie一起访问,但是Referer我无法理解,但必须要设置。
//也就是必须指定它的Referer必须为当前访问的URL
get.setHeader("Referer", mianUrl);
get.addHeader("Cookie", cookie);
try {
//获取Get响应,如果状态码是200的话表示连接成功
HttpResponse httpResponse = new DefaultHttpClient().execute(get);
if(httpResponse.getStatusLine().getStatusCode() == 200){
HttpEntity entity = httpResponse.getEntity();
//获取纯净的主页HTML源码,这里大家可以将mianhtml定义在其他地方
String mainhtml = EntityUtils.toString(entity);
}
到这里呢,基本上就等于完事了,获取到主页的HTML纯文本之后就是通过Jsoup包进行解析了,不会使用的话可以去查下它的API文档。
第二步:
下面我们就开始查询我们的成绩的了,首先呢还是HttpWatch工具,依次点击信息查询中的成绩查询,然后点击按学期查询就会查询到在校所有成绩了。
文章图片
这里我们依然会发现因为是按了按钮,所以传递了值,所以还是Post请求,不是状态码是 200。 Post的URL是http://jwxt.jit.edu.cn/xscj_gc.aspx?xh=1205107009&xm=陈凯&gnmkdm=N121605 这里呢依然头疼的问题是URL的获取,我思索观察了半天,发现URL竟然在刚刚获取的mainhtml文本中,是作为按钮跳转的地址。所以这个时候我们就需要用Jsoup来解析这个网页源码了。看代码:
//分析html
Document doc = Jsoup.parse(mainhtml);
//获取所有链接
Elements links = doc.select("a[href]");
//创建一个缓冲区
StringBuffer sff = new StringBuffer();
for(Element link : links){
//获取所要查询的URL,这里对应地址按钮的名字叫成绩查询
if(link.text().equals("成绩查询"))
//获取所要查询的相对地址,获取相对的地址
sff.append(link.attr("href"));
}String str = sff.toString();
//返回查询的URL,将主页地址与相对地址连接起来,同样这里的cjcxUrl可以定义在外面
String cjcxUrl = login_url+ "/" + str ;
好了,这样成绩查询的URL就这么出来了。下面是Post请求。在下面标签Stream中查看到传递的参数是Button1="%B0%B4%D1%A7%C6%DA%B2%E9%D1%AF",表示点下了按学期查询,ddlXN和ddlXQ都是空,如果你们学校教务有默认了值,你就按照你们教务的填上就好了。下面是__VIEWSTATE的值,虽然上面出现的是第二种值的情况,但是我这里还是以第一种为例子,我估计你们的学校也许不会出现这种情况。
public static List paramsgra1 = new ArrayList();
//可以发现这是第一种情况值的传递,虽然两种情况不一样,但是放心只要是你们学校的学生,登陆的时候值的情况都是两种,且值都是相同的。
paramsgra1.add(new BasicNameValuePair("__VIEWSTATE","dDwxNjgwNjIxMzEzO3Q8cDxsPHhoOz47bDwxMjA1MTA3MDA5Oz4+O2w8aTwxPjs+O2w8dDw7bDxpPDE+O2k8Mz47aTw1PjtpPDc+O2k8OT47aTwxMT47aTwxMz47aTwxNj47aTwyNj47aTwyNz47aTwyOD47aTwzND47aTwzNj47aTwzOD47aTwzOT47aTw0MT47aTw0Mj47aTw0ND47aTw0Nj47aTw0OD47aTw2MD47aTw2NT47PjtsPHQ8cDxwPGw8VGV4dDs+O2w85a2m5Y+377yaMTIwNTEwNzAwOTs+Pjs+Ozs+O3Q8cDxwPGw8VGV4dDs+O2w85aeT5ZCN77ya6ZmI5YevOz4+Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDzlrabpmaLvvJrkv6Hmga/mioDmnK/lrabpmaI7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPOS4k+S4mu+8mjs+Pjs+Ozs+O3Q8cDxwPGw8VGV4dDs+O2w86L2v5Lu25bel56iLKOacrCk7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPOihjOaUv+ePre+8mjEy6L2v5Lu25bel56iL77yIMe+8iTs+Pjs+Ozs+O3Q8cDxwPGw8VGV4dDs+O2w8MjAxMjA1MDc7Pj47Pjs7Pjt0PHQ8O3Q8aTwxNT47QDxcZTsyMDAxLTIwMDI7MjAwMi0yMDAzOzIwMDMtMjAwNDsyMDA0LTIwMDU7MjAwNS0yMDA2OzIwMDYtMjAwNzsyMDA3LTIwMDg7MjAwOC0yMDA5OzIwMDktMjAxMDsyMDEwLTIwMTE7MjAxMS0yMDEyOzIwMTItMjAxMzsyMDEzLTIwMTQ7MjAxNC0yMDE1Oz47QDxcZTsyMDAxLTIwMDI7MjAwMi0yMDAzOzIwMDMtMjAwNDsyMDA0LTIwMDU7MjAwNS0yMDA2OzIwMDYtMjAwNzsyMDA3LTIwMDg7MjAwOC0yMDA5OzIwMDktMjAxMDsyMDEwLTIwMTE7MjAxMS0yMDEyOzIwMTItMjAxMzsyMDEzLTIwMTQ7MjAxNC0yMDE1Oz4+Oz47Oz47dDxwPDtwPGw8b25jbGljazs+O2w8d2luZG93LnByaW50KClcOzs+Pj47Oz47dDxwPDtwPGw8b25jbGljazs+O2w8d2luZG93LmNsb3NlKClcOzs+Pj47Oz47dDxwPHA8bDxWaXNpYmxlOz47bDxvPHQ+Oz4+Oz47Oz47dDxAMDw7Ozs7Ozs7Ozs7Pjs7Pjt0PEAwPDs7Ozs7Ozs7Ozs+Ozs+O3Q8QDA8Ozs7Ozs7Ozs7Oz47Oz47dDxAMDw7Ozs7Ozs7Ozs7Pjs7Pjt0PEAwPDs7Ozs7Ozs7Ozs+Ozs+O3Q8QDA8Ozs7Ozs7Ozs7Oz47Oz47dDxAMDw7Ozs7Ozs7Ozs7Pjs7Pjt0PEAwPHA8cDxsPFZpc2libGU7PjtsPG88Zj47Pj47Pjs7Ozs7Ozs7Ozs+Ozs+O3Q8QDA8cDxwPGw8VmlzaWJsZTs+O2w8bzxmPjs+Pjs+Ozs7Ozs7Ozs7Oz47Oz47dDxwPHA8bDxUZXh0Oz47bDxZUFhZOz4+Oz47Oz47dDxAMDw7Ozs7Ozs7Ozs7Pjs7Pjs+Pjs+Pjs+BX9xNmfFtmx2lSdmzHhoR2MpxKw="));
//paramsgra1.add(new BasicNameValuePair("__VIEWSTATE", "/wEPDwULLTE0NDU1MDczNjEPFgIeAnhoBQoxMjA1MTA3MDA5FgICAQ9kFiwCAQ8PFgIeBFRleHQFE+WtpuWPt++8mjEyMDUxMDcwMDlkZAIDDw8WAh8BBQ/lp5PlkI3vvJrpmYjlh69kZAIFDw8WAh8BBRvlrabpmaLvvJrkv6Hmga/mioDmnK/lrabpmaJkZAIHDw8WAh8BBQnkuJPkuJrvvJpkZAIJDw8WAh8BBRHova/ku7blt6XnqIso5pysKWRkAgsPDxYCHwEFIeihjOaUv+ePre+8mjEy6L2v5Lu25bel56iL77yIMe+8iWRkAg0PDxYCHwEFCDIwMTIwNTA3ZGQCEA8QZBAVDwAJMjAwMS0yMDAyCTIwMDItMjAwMwkyMDAzLTIwMDQJMjAwNC0yMDA1CTIwMDUtMjAwNgkyMDA2LTIwMDcJMjAwNy0yMDA4CTIwMDgtMjAwOQkyMDA5LTIwMTAJMjAxMC0yMDExCTIwMTEtMjAxMgkyMDEyLTIwMTMJMjAxMy0yMDE0CTIwMTQtMjAxNRUPAAkyMDAxLTIwMDIJMjAwMi0yMDAzCTIwMDMtMjAwNAkyMDA0LTIwMDUJMjAwNS0yMDA2CTIwMDYtMjAwNwkyMDA3LTIwMDgJMjAwOC0yMDA5CTIwMDktMjAxMAkyMDEwLTIwMTEJMjAxMS0yMDEyCTIwMTItMjAxMwkyMDEzLTIwMTQJMjAxNC0yMDE1FCsDD2dnZ2dnZ2dnZ2dnZ2dnZ2RkAhoPD2QWAh4Hb25jbGljawUPd2luZG93LnByaW50KCk7ZAIbDw9kFgIfAgUPd2luZG93LmNsb3NlKCk7ZAIcDw8WAh4HVmlzaWJsZWdkZAIiDzwrAAsAZAIkDzwrAAsAZAImDzwrAAsAZAInDzwrAAsAZAIpDzwrAAsAZAIqDzwrAAsAZAIsDzwrAAsAZAIuDzwrAAsBAA8WAh8DaGRkAjAPPCsACwEADxYCHwNoZGQCPA8PFgIfAQUEWVBYWWRkAkEPPCsACwBkZDO/nriqRqGoEf9J81ywNopcRE6l"));
//paramsgra1.add(new BasicNameValuePair("__EVENTVALIDATION", "/wEWGwKpihYC7sDphAUC/Onx6gUC/+nt/AsC9umZ6QQC+em18woC+Omh6QcC++nd8wUC8unJ6QYC9emlkAYC+unNqwgCoKvKxQ4Co6uOhAgCoquSxAkCvavWhAsCvKu6xAgC38DphAUC0K/D6gkC0a/D6gkC0q/D6gkCjOeKxgYCoMKT8Q0Cu6uxhggCz4a6sQ8Chdn12wMC1pTPmwIC6u/XxglYyInHMSCwi1+Sj12nmKQ31hu7wg=="));
paramsgra1.add(new BasicNameValuePair("Button1","%B0%B4%D1%A7%C6%DA%B2%E9%D1%AF"));
paramsgra1.add(new BasicNameValuePair("ddlXQ",""));
paramsgra1.add(new BasicNameValuePair("ddlXN",""));
以上就是你要进行成绩查询所要Post的值,下面呢进行Post请求
HttpPost cjpost = new HttpPost(login_cjcx);
//这一步是关键,跟刚刚一样,这里需要将cookie和Referer的设置一下,其他的话,不设置应该也没什么事,他们都是在HttpWatch的Post头信息中,你们自己去看看吧
cjpost.setHeader("Referer", login_cjcx);
cjpost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3;
WOW64;
Trident/7.0;
rv:11.0) like Gecko");
cjpost.setHeader("Content-Type","application/x-www-form-urlencoded");
cjpost.setHeader("Accept", "text/html, application/xhtml+xml, */*");
cjpost.setHeader("Connection", "Keep-Alive");
cjpost.addHeader("Cookie",connJK.loginCookie());
try{
//Post请求获取成绩的Html。将刚刚的成绩查询的List拿过来,设置编码。
cjpost.setEntity(new UrlEncodedFormEntity(paramsgra1, "gb2312"));
HttpResponse response = new DefaultHttpClient().execute(cjpost);
HttpEntity entity = response.getEntity();
//获取成绩的HTML源码
cjHtml = EntityUtils.toString(entity);
//自己打印出来看看
//System.out.println(cjHtml);
//关闭连接
cjpost.abort();
}
经过上面一系列的讲解,我们就可以获取成绩的HTML源码了,里面都是我们所需要的成绩。 同理,下面我们进行解析。
//处理获取的分数页面
Document cjdoc = Jsoup.parse(cjHtml);
// 获取到每行数据的选择器,这里的选择器你们可以观察下HTML代码,这里就不多说了。
String rowRegex = "div.main_box div.mid_box span.formbox table#Datagrid1.datelist tbody tr";
// 每行的数据元素
Elements rowElements = cjdoc.select(rowRegex);
List