前言:今天1024程序员节,便想抽出时间把前段时间做的小东西写成一个系列记录下来,以后有时间会迭代更新。这里分两部分,一:Java Web后台接口开发,主要分模拟登录,破解正方教务网登录验证码,抓取教务网数据,最后将这些封装成json API 接口给Android客户端调用。二:Android客户端调用接口,主要是写前端工作,也是最终呈现给用户的。
声明:转载请注明出处。
开发环境 :
- Myeclipse10+apache-tomcat-6.0.44
- 抓包工具fiddler 4(其实Firefox抓取也足够用,只是对中文会乱码)
- 开发中用到的包(源代码后面会给出),其中jsoup-1.8.3.jar在后面对抓取到的html数据进行解析会用到。
- 这是工程目录
HttpClient的工具类
//单例模式得到HttpClient的对象,会自动帮我们管理cookie。
public class JwUtils {
private static CloseableHttpClient httpClient = null;
public final static String BASE_URL = "http://jw1.hustwenhua.net/";
public static CloseableHttpClient getHttpClient() {
if (httpClient == null){
httpClient = HttpClients.createDefault();
return httpClient;
}
return httpClient;
}
}
首先我们分析会发现在浏览器输入教务网址:http://jw1.hustwenhua.net,它会变成http://jw1.hustwenhua.net/(oem5ln55gp0jac2njop2tmzo)/default2.aspx。中间会多出“(oem5ln55gp0jac2njop2tmzo)”这么一段字符串,这一段字符串很重要,以后每次访问都要带上它。而且等你下次打开浏览器在访问教务网这段字符串又不一样。这段字符串可以理解为一次有效的访问时期。
用fidder4抓取得到登录需要传递的参数如下:
POST请求,请求网址为:“http://jw1.hustwenhua.net/(oem5ln55gp0jac2njop2tmzo)/default2.aspx”
这里就就解释下两个,_VIEWSTATE是以后每次抓取像教务网发起请求都要传递的一段字符串,它隐藏在每一个页面的一个表单里,所以我们首先可以通过Get请求先得到这段字符串。txtSecretCode是要传递的验证码。不多说直接贴代码。
public class JwLogin {
/**
* 通过Post请求得到Location中的头部请求的实际网址信息,解析出中间那段上面说的很重要的字符串,作用是标志这是一次有效访问。
* 在通过Get请求解析出隐藏在当前页面的__VIEWSTATE的值
* @return
*/
public static Map<String, String> getBaseValue() {
Map<String, String> map = new HashMap<String, String>();
CloseableHttpClient hc = JwUtils.getHttpClient();
HttpGet hg = null;
HttpPost hp = new HttpPost(JwUtils.BASE_URL);
HttpResponse responseGet = null;
HttpResponse responsePost = null;
try {
responsePost = hc.execute(hp);
if("HTTP/1.1 302 Found".equals(responsePost.getStatusLine().toString())){
Header lheader = responsePost.getFirstHeader("Location");
String urlcode = lheader.getValue();
urlcode = urlcode.substring(urlcode.indexOf("("), (urlcode.indexOf(")")+1));
map.put("urlcode", urlcode);
hg = new HttpGet(JwUtils.BASE_URL+urlcode+"/default2.aspx");
responseGet = hc.execute(hg);
if ("HTTP/1.1 200 OK".equals(responseGet.getStatusLine().toString())) {
HttpEntity entity = responseGet.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity, "utf-8");
org.jsoup.nodes.Document doc = Jsoup.parse(result);
map.put("viewstate", doc.select("input[name=__VIEWSTATE]").val());
}
}
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(hg != null){
hg.abort();
}
if(hp != null){
hp.abort();
}
}
return map;
}
/**
*
* @param hm 用户名
* @param pwd 密码
* @param role 用户类型
* @param secretcode 验证码
* @param viewstate 页面隐藏的__VIEWSTATE的值
* @param urlcode 网址中那段标志是此次有效请求的值
* @return 返回登录用户的用户名,在后面的抓取别的数据有用
*/
public static String isLogin(String hm, String pwd, String role, String secretcode, String viewstate, String urlcode) {
CloseableHttpClient hc = JwUtils.getHttpClient();
HttpPost hp = new HttpPost(JwUtils.BASE_URL+urlcode+"/default2.aspx");
HttpResponse responsePost = null;
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("__VIEWSTATE", viewstate));
params.add(new BasicNameValuePair("txtUserName", hm));
params.add(new BasicNameValuePair("TextBox2", pwd));
params.add(new BasicNameValuePair("txtSecretCode", secretcode));
params.add(new BasicNameValuePair("RadioButtonList1", role));
params.add(new BasicNameValuePair("Button1", ""));
// params.add(new BasicNameValuePair("lbLanguage", ""));
// params.add(new BasicNameValuePair("hidPdrs", ""));
// params.add(new BasicNameValuePair("hidsc", ""));
try {
hp.setEntity(new UrlEncodedFormEntity(params));
responsePost = hc.execute(hp);
// 获得跳转的网址
Header locationHeader = responsePost.getFirstHeader("Location");
if (locationHeader != null && "HTTP/1.1 302 Found".equals(responsePost.getStatusLine().toString())) {
String login_success = locationHeader.getValue();// 获取登陆成功之后跳转链接
HttpGet httpget = new HttpGet(JwUtils.BASE_URL+login_success);
HttpResponse re2 = hc.execute(httpget);
Document doc = Jsoup.parse(EntityUtils.toString(re2.getEntity(), "utf-8"));
Element e = doc.getElementById("xhxm");
if(e==null){
return null;
}else{
System.out.println("登陆成功");
return e.text();
}
} else{
System.out.println("登陆不成功,请稍后再试!");
return null;
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(hp != null){
hp.abort();
}
}
return null;
}
}
项目源码:
CSDN: http://download.csdn.net/detail/sinat_18127633/9210327
GitHub: https://github.com/zhugusheng/whjw