网页抓取数据(就是上文中的验证码地址()数据解析)
优采云 发布时间: 2022-01-31 16:25网页抓取数据(就是上文中的验证码地址()数据解析)
VERIFATIONURL就是上面的验证码地址。首先创建一个post请求,然后创建一个Httpclient,然后HttpResponse就是响应。我们得到的服务器的返回值是通过他得到的,我们创建一个字节数组。首先将响应中得到的数据转换成字节数组,然后通过BitmapFactory.decodeByteArray(bytes, 0, bytes.length);将字节数组编译成位图;方法。这时候我们拿到验证码的Bitmap,直接ImageView.SetBitmap可以吗?当然不是。首先,我们在网页上获取图片是一个耗时的操作,所以我们不能在主线程中进行,第二点是我们只能在主线程中更新UI。我们使用 Thread+Handler 解决方案。以上代码:
private void DoGetVerifation() {
new Thread(new Runnable() {
@Override
public void run() {
HttpPost httPost = new HttpPost(VERIFATIONURL);
HttpClient client = new DefaultHttpClient();
try {
HttpResponse httpResponse = client.execute(httPost);
byte[] bytes = new byte[1024];
bytes = EntityUtils.toByteArray(httpResponse.getEntity());
bmVerifation = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
Message msg = new Message();
msg.arg1 = 10;
handler.sendMessage(msg);
}
}).start();
}
在主线程接收到msg后,ivVerifation.setImageBitmap(bmVerifation); 所以我们得到验证码的图片并显示在主界面上
2.发送 Post 请求登录
我们现在知道用户名、密码。验证码,下一步就是登录了,登录是一个耗时的操作。当然,必须启动一个新线程。这个没什么好说的,我们也是在发送一个Post请求,上面的代码:
private void DoLogin(final String user, final String password, final String verifation) {
new Thread(new Runnable() {
@Override
public void run() {
DefaultHttpClient defaultclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(LOGINURL);
HttpResponse httpResponse;
//设置post參数
List params = new ArrayList();
params.add(new BasicNameValuePair("__VIEWSTATE", "dDwyODE2NTM0OTg7Oz6ZmvWn7xzjizifHN9MgLoDNTRtjQ=="));
params.add(new BasicNameValuePair("Button1", ""));
params.add(new BasicNameValuePair("hidPdrs", ""));
params.add(new BasicNameValuePair("hidsc", ""));
params.add(new BasicNameValuePair("lbLanguage", ""));
params.add(new BasicNameValuePair("RadioButtonList1", "%D1%A7%C9%FA"));
params.add(new BasicNameValuePair("TextBox2", password));
params.add(new BasicNameValuePair("txtSecretCode", verifation));
params.add(new BasicNameValuePair("txtUserName", user));
//获得个人主界面的HTML
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
httpResponse = defaultclient.execute(httpPost);
Log.i("xyz", String.valueOf(httpResponse.getStatusLine().getStatusCode()));
if (httpResponse.getStatusLine().getStatusCode() == 200) {
StringBuffer sb = new StringBuffer();
HttpEntity entity = httpResponse.getEntity();
MAINBODYHTML = EntityUtils.toString(entity);
IsLoginSuccessful(MAINBODYHTML);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
首先是创建post,客户端响应,和上一个一样。我们收到了对数据的 Post 请求。比如获取验证码的请求不需要参数,但是我们需要将验证码发送到服务器用户名密码进行登录,所以我们请求的是Post。设置参数
//设置post參数
List params = new ArrayList();
params.add(new BasicNameValuePair("__VIEWSTATE", "dDwyODE2NTM0OTg7Oz6ZmvWn7xzjizifHN9MgLoDNTRtjQ=="));
params.add(new BasicNameValuePair("Button1", ""));
params.add(new BasicNameValuePair("hidPdrs", ""));
params.add(new BasicNameValuePair("hidsc", ""));
params.add(new BasicNameValuePair("lbLanguage", ""));
params.add(new BasicNameValuePair("RadioButtonList1", "%D1%A7%C9%FA"));
params.add(new BasicNameValuePair("TextBox2", password));
params.add(new BasicNameValuePair("txtSecretCode", verifation));
params.add(new BasicNameValuePair("txtUserName", user));
//获得个人主界面的HTML
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
httpResponse = defaultclient.execute(httpPost);
Log.i("xyz", String.valueOf(httpResponse.getStatusLine().getStatusCode()));
if (httpResponse.getStatusLine().getStatusCode() == 200) {
StringBuffer sb = new StringBuffer();
HttpEntity entity = httpResponse.getEntity();
MAINBODYHTML = EntityUtils.toString(entity);
IsLoginSuccessful(MAINBODYHTML);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
这些参数就是我们之前使用HttpWatch获取的Post数据。只有带类型的参数,没有值。我们将它们设置为 "" 为空,并使用 httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); 方法来设置我们的 set param 参数集给 httppost。以下与之前,也是通过Response获取response值,但是不是图片而是吃瓜果的String=-=。函数IsLoginSuccessful是用来推断我们是否登录成功的。。这会用到我们将要进行的数据分析下面说说。
四、数据分析
1.返回的响应?
登录后,我们会得到服务器给我们的响应。我们成功登录了火狐的Firebug的模型(HttpWatch也可以个人觉得看响应值Firebug比较方便)。让我们看看他会给我们带来什么
如图所示,我们可以知道响应值其实就是我们网页的HTML。假设登录成功,其实是返回个人主页
那么假设失败呢?我们模拟一个验证码错误,如下图。可见同样返回一个 HTML
我们注意到这条线
alert('验证码不对!。');document.getElementById('TextBox2').focus();
这实际上会弹出一个表单,告诉我们验证码是错误的。下面我们将使用里面的不同提示来推断我们登录的状态
2.使用Jsoup进行数据解析
我们创建前面提到的 IsLoginSuccessful 函数
private void IsLoginSuccessful(String loginresult) {
Document doc = Jsoup.parse(loginresult);
Elements alert = doc.select("script[language]");
Elements success = doc.select("a[href]");
Message msg = new Message();
//先推断是否登录成功。若成功直接退出
for (Element link : success) {
//获取所要查询的URL,这里相应地址button的名字叫成绩查询
if (link.text().equals("*敏*感*词*查询")) {
Log.i("xyz", "登录成功");
msg.arg1 = 6;
handler.sendMessage(msg);
return;
}
}
for (Element link : alert) {
//刷新验证码
DoGetVerifation();
//获取错误信息
if (link.data().contains("验证码不对")) {
Log.i("xyz", "验证码错误");
msg.arg1 = 0;
handler.sendMessage(msg);
} else if (link.data().contains("username不能为空")) {
Log.i("xyz", "username不能为空");
msg.arg1 = 1;
handler.sendMessage(msg);
} else if (link.data().contains("password错误")) {
Log.i("xyz", "password或username错误");
msg.arg1 = 2;
handler.sendMessage(msg);
} else if (link.data().contains("password不能为空")) {
Log.i("xyz", "password不能为空");
msg.arg1 = 3;
handler.sendMessage(msg);
......
}
}
我正在使用开源库 Jsoup 进行解析。当然,有很多方法可以做到这一点。你可以自己google一下(百度=-=你这个老躺*敏*感*词*)
你可以看看Jsoup的中文API指南
首先,我们推断登录成功。我们在成功登录的 HTML 中发现了以下语句。
*敏*感*词*查询
我们通过Elements success = doc.select("a[href]"); 获取以a href" 开头的数据,然后循环推断是否有等级测试查询,假设存在。必须返回的响应是我们的个人主页。也就是说登录成功了,我们在主线程toast中使用Thread+Handler来提醒用户
然后我们通过Elements alert = doc.select("script[language]"); 得到脚本开头的语句,同样的循环推理,这里需要注意的是它和a href 不同。我们使用的是link.data(),前一个是link.text();
JsoupAPI 是这样写的
五、访问个人信息
我们登录了个人主页,想进一步了解自己,比如四年级和六年级,该怎么办?相同或获取请求
获取验证码网址的方法相同。我们得到了成绩单的网址
xh=2014117170&xm=">(dlxrmg55j21wlaqv2z5rcdyi)/xsdjkscx.aspx?xh=2014117170&xm=邹德宏&gnmkdm=N121606
前面的(dlxrmg55j21wlaqv2z5rcdyi)/是我们访问的主机不变。我们所要做的就是获取 xsdjkscx.aspx?
xh=2014117170&xm=邹德宏&gnmkdm=N121606. 这个问题困扰我很久了。后来突然发现,万智居然是在前面提到的登'lu'cheng'登录成功后返回的html中。所以还是用Jsoup来分析
Document doc = Jsoup.parse(MAINBODYHTML);
Elements links = doc.select("a[href]");
StringBuffer sb = new StringBuffer();
for (Element link : links) {
//获取所要查询的URL,这里相应地址button的名字叫成绩查询
if (link.text().equals("*敏*感*词*查询")) {
sb.append(link.attr("href"));
}
}
GETSCOREURL = sb.toString();
GETSCOREURL 是我们想要的地址的后半部分,然后我们添加前半部分来获取 Post 请求。
这里返回的是一个 Html 表
学年学期*敏*感*词*名称准考证号考试日期成绩听力成绩阅读成绩写作成绩综合成绩
2014-20152CET4610041151112625201506483167
1731430
2015-20161CET6610041152209503201512376126
1431070
同样使用 Jsoup
private void parse(String parse) {
Document doc = Jsoup.parse(parse);
Elements trs = doc.select("table").select("tr");
for (int i = 0; i < trs.size(); i++) {
Elements tds = trs.get(i).select("td");
for (int j = 0; j < tds.size(); j++) {
String text = tds.get(j).text();
score[i][j] = text;
Log.i("xyz", score[i][j]);
}
}
}
解析后,我们得到一个分数数组
六、总结
博主高二党学习Android开发一年半了,现在终于有了上手的感觉。本教程对于新手来说会有些困难。不过你要保持冷静,博主第一次下载HttpWatch的时候愣了一下。什么都不懂,慢慢摸索后终于有了一点线索。好的,仅此而已。有邮件交流的朋友加我=-=