赞
踩
以下文章可能有参考别人的代码而汇总的内容 请各位大侠合作愉快 借鉴一下
http://blog.csdn.net/qq_26891045/article/details/52517585
http://blog.csdn.net/dongnan591172113/article/details/51832628
http://www.360doc.com/content/14/0328/03/13017437_364313510.shtml
http://blog.csdn.net/r3t7o7/article/details/57074722
http://www.cnblogs.com/hoojo/archive/2013/04/12/3016516.html
http://blog.csdn.net/onafioo/article/details/49923827
http://www.knowsky.com/367400.html
https://bbs.pediy.com/thread-51945.htm
http://www.360doc.com/content/14/0328/03/13017437_364313510.shtml
http://blog.csdn.net/enweitech/article/details/51849049
http://yongkuang.iteye.com/blog/1172100
http://www.myexception.cn/java-web/42304.html
https://item.taobao.com/item.htm?spm=a230r.1.14.53.34d59246nH0FYS&id=558588943852&ns=1&abbucket=5#detail
http://blog.csdn.net/kongwei521/article/details/54927689
https://zhidao.baidu.com/question/165093593.html
https://s.taobao.com/search?q=.net+%E5%8F%8D%E7%BC%96%E8%AF%91&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20170921&ie=utf8
https://item.taobao.com/item.htm?spm=a230r.1.14.44.4e9ae445cQsYdX&id=556004656489&ns=1&abbucket=5#detail
http://blog.csdn.net/dianacody/article/details/39584977 很好的
http://blog.csdn.net/wangpeng047/article/details/19624529
=================================================================
package pachong;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class CrawlerUtil {
/*
* 获取源代码
*/
public static String getHtmlSource(String url){
InputStream inputStream = null;
BufferedReader in = null;
StringBuilder htmlSource= new StringBuilder();
try {
//1.获取网址
URL u = new URL(url);
//2.打开连接
URLConnection conn = u.openConnection();
//3.获取输入流
inputStream = conn.getInputStream();
//4.将源代码写入内存(设置编码)
in = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
String str = "";
while((str = in.readLine()) != null){
htmlSource.append(str);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//关闭I/o
try {
if(in != null)in.close();
if(inputStream != null)inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return htmlSource.toString();
}
public static void main(String[] args) {
String url = "http://www.mnkjxy.com/";
System.out.println(getHtmlSource(url));
}
}
说明:
后者 可以生产连接对象,调用其它方法做一些事情功能使用。
=======================================================
下面转载httpclient ,感谢下面这位大咔 wangpeng047
http://blog.csdn.net/wangpeng047/article/details/19624529
http://blog.csdn.net/witsmakemen/article/details/12003793
传递参数:给服务器 , 在测试返回的数据
TestingCode:
//创建默认的httpClient实例.
HttpClient httpclient = new DefaultHttpClient();
//创建httppost
HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
//创建参数队列
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("type", "house"));
UrlEncodedFormEntity uefEntity;
try {
uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(uefEntity);
System.out.println("executing request " + httppost.getURI());
HttpResponse response;
response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity != null) { System.out.println("--------------------------------------");
System.out.println("Response content: " + EntityUtils.toString(entity,"UTF-8"));
System.out.println("--------------------------------------");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
}catch(UnsupportedEncodingException e1) {
e1.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally{
//关闭连接,释放资源
httpclient.getConnectionManager().shutdown();
}
输出:
模拟表单登陆:
TestingCode:
//创建默认的httpClient实例.
HttpClient httpclient = new DefaultHttpClient();
//创建httppost
HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
//创建参数队列
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("username", "admin"));
formparams.add(new BasicNameValuePair("password", "123456"));
UrlEncodedFormEntity uefEntity;
try {
uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(uefEntity);
System.out.println("executing request " + httppost.getURI());
HttpResponse response;
response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity != null) { System.out.println("--------------------------------------");
System.out.println("Response content: " + EntityUtils.toString(entity,"UTF-8"));
System.out.println("--------------------------------------");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
}catch(UnsupportedEncodingException e1) {
e1.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally{
//关闭连接,释放资源
httpclient.getConnectionManager().shutdown();
}
会将返回的jsp输出:
--------------------------------------------------------------
package pachong;
import java.io.IOException;
import java.net.URISyntaxException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
/*
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.CookieSpec;
import org.apache.http.cookie.CookieSpecProvider;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.cookie.BestMatchSpecFactory;
import org.apache.http.impl.cookie.BrowserCompatSpec;
import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
*/
public class Weibo163
{
public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException
{
HttpClient httpclient = new DefaultHttpClient();
try
{
HttpGet httpget = new HttpGet("http://www.baidu.com");
System.out.println("executing request " + httpget.getURI());
HttpResponse response = httpclient.execute(httpget);
// 打印响应状态
System.out.println(response.getStatusLine());
System.out.println("--------------------------------------");
// 获取响应实体
HttpEntity entity = response.getEntity();
if (entity != null)
{
// 打印响应内容长度
System.out.println("Response content length: " + entity.getContentLength());
// 打印响应内容
System.out.println("Response content: " + EntityUtils.toString(entity,"UTF-8"));
}
System.out.println("------------------------------------");
} finally
{
// 关闭连接,释放资源
httpclient.getConnectionManager().shutdown();
}
}
}
===========================================
自己写的 读取网页数据的 模板代码:
package pachong2;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import pachong.Almanac;
public class TestHtml
{
public TestHtml()
{
}
private static void doTask(String url)
{
String html = pickData(url);
String s1 = analyzeHtml(html);
System.out.println(s1);
// String s2 = analyzeHtml(html);
}
// 爬取网页全部数据 getHtmlEntity
private static String pickData(String url)
{
CloseableHttpClient httpclient = HttpClients.createDefault();
try
{
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpget);
try
{
// 获取响应实体
HttpEntity entity = response.getEntity();
// 打印响应状态
if (entity != null)
{
return EntityUtils.toString(entity);
}
} finally
{
response.close();
}
} catch (ClientProtocolException e)
{
e.printStackTrace();
} catch (ParseException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} finally
{
// 关闭连接,释放资源
try
{
httpclient.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
return null;
}
// 解析网页中的元素与数据,提取出来。
private static String analyzeHtml(String html)
{
org.jsoup.nodes.Document document = Jsoup.parse(html);
String a, b, c,d="";
// Element element =document.getElementById(" 此处输入某一个网页标签代码 id=** ");
// a = element.text().toString(); //获取标签的值了
b = analyzeHtml(document, "Layer2");
//b = analyzeHtml(document, "__01");
return b;
/*
String solarDate, lunarDate, chineseAra, should, avoid = " ";
org.jsoup.nodes.Document document = Jsoup.parse(html);
// 公历时间
//solarDate = getSolarDate();
// 农历时间
Element eLunarDate = document.getElementById("info_nong");
lunarDate = eLunarDate.child(0).html().substring(1, 3) + eLunarDate.html().substring(11);
// 天干地支纪年法
Element eChineseAra = document.getElementById("info_chang");
chineseAra = eChineseAra.text().toString();
// 宜
// should = getSuggestion(document, "yi");
// 忌
// avoid = getSuggestion(document, "ji");
// Almanac almanac = new Almanac(solarDate, lunarDate, chineseAra, should, avoid);
return almanac;
*/
}
private static String analyzeHtml(org.jsoup.nodes.Document document,String id){
Element element=document.getElementById(id);
Elements elements=element.getElementsByTag("a");
StringBuffer sb=new StringBuffer();
for (Element e : elements) {
sb.append(e.text()+" ");
}
return sb.toString();
}
public static void main(String[] args)
{
TestHtml.doTask("http://www.mnkjxy.com/html/xwzx/xyxw/");
}
}
===============================
httpclient学习知识汇总:
HttpClient 不是一个浏览器。它是一个客户端的 HTTP 通信实现库。 HttpClient 的目
标是发送和接收 HTTP 报文。
HttpClient 最重要的功能是执行 HTTP 方法。一个 HTTP 方法的执行包含一个或多个 HTTP请求/HTTP 响应交换,通常由 HttpClient 的内部来处理。而期望用户提供一个要执行的请求对象,而 HttpClient 期望传输请求到目标服务器并返回对应的响应对象,或者当执行不成功时抛出异常。
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int l;
byte[] tmp = new byte[2048];
while ((l = instream.read(tmp)) != -1) {
}
}
------------------------------------------------------------------
HTTP 请求
HttpGet httpget = new HttpGet(
"http://www.google.com/search?hl=en&q=httpclient&btnG=Google
+Search&aq=f&oq=");
HttpClient 提供很多工具方法来简化创建和修改执行 URI。URI 也可以编程来拼装:
URI uri = URIUtils.createURI("http", "www.google.com", -1,
"/search",
"q=httpclient&btnG=Google+Search&aq=f&oq=", null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());
查询字符串也可以从独立的参数中来生成:
List<NameValuePair> qparams = new ArrayList<NameValuePair>();
qparams.add(new BasicNameValuePair("q", "httpclient"));
qparams.add(new BasicNameValuePair("btnG", "Google Search"));
qparams.add(new BasicNameValuePair("aq", "f"));
qparams.add(new BasicNameValuePair("oq", null));
URI uri = URIUtils.createURI("http", "www.google.com", -1,
"/search",
URLEncodedUtils.format(qparams, "UTF-8"), null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());
HTTP 响应:HTTP 响应是由服务器在接收和解释请求报文之后返回发送给客户端的报文。响应报文
的第一行包含了协议版本,之后是数字状态码和相关联的文本段。
HttpResponse response = new
BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString());
处理报文头部
一个 HTTP 报文可以包含很多描述如内容长度,内容类型等信息属性的头部信息。
HttpClient 提供获取,添加,移除和枚举头部信息的方法。
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");
Header h1 = response.getFirstHeader("Set-Cookie");
System.out.println(h1);
Header h2 = response.getLastHeader("Set-Cookie");
System.out.println(h2);
Header[] hs = response.getHeaders("Set-Cookie");
System.out.println(hs.length);
获得给定类型的所有头部信息最有效的方式是使用 HeaderIterator 接口。
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie","c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie","c2=b; path=\"/\", c3=c; domain=\"localhost\"");
HeaderIterator it = response.headerIterator("Set-Cookie");
while (it.hasNext()) {
System.out.println(it.next());
}
它也提供解析 HTTP 报文到独立头部信息元素的方法方法。
HttpResponse response = new
BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie",
"c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie",
"c2=b; path=\"/\", c3=c; domain=\"localhost\"");
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator("Set-Cookie"));
while (it.hasNext()) {
HeaderElement elem = it.nextElement();
System.out.println(elem.getName() + " = " +
elem.getValue());
NameValuePair[] params = elem.getParameters();
for (int i = 0; i < params.length; i++) {
System.out.println(" " + params[i]);
}
}
HTTP 实体
HTTP 报文可以携带和请求或响应相关的内容实体。实体可以在一些请求和响应中找到,因为它们也是可选的。使用了实体的请求被称为封闭实体请求。 HTTP 规范定义了两种封闭实体的方法: POST 和 PUT。响应通常期望包含一个内容实体。这个规则也有特例,比如HEAD 方法的响应和 204 No Content, 304 Not Modified 和 205 Reset Content响应。
HttpClient 根据其内容出自何处区分三种类型的实体:
? streamed 流式:内容从流中获得,或者在运行中产生。特别是这种分类包含从 HTTP响应中获取的实体。流式实体是不可重复生成的。
? self-contained 自我包含式:内容在内存中或通过独立的连接或其它实体中获得。自我包含式的实体是可以重复生成的。这种类型的实体会经常用于封闭 HTTP 请求的实体。
? wrapping 包装式:内容从另外一个实体中获得。
重复实体
实体可以重复,意味着它的内容可以被多次读取。这就仅仅是自我包含式的实体了(像ByteArrayEntity 或 StringEntity)。
使用 HTTP 实体
因为一个实体既可以代表二进制内容又可以代表字符内容,它也支持字符编码(支持后者也就是字符内容)。实体是当使用封闭内容执行请求,或当请求已经成功执行,或当响应体结果发功到客户端时创建的。
要从实体中读取内容,可以通过 HttpEntity#getContent()方法从输入流中获取,这 会 返 回 一 个 java.io.InputStream 对 象 , 或 者 提 供 一 个 输 出 流 到HttpEntity#writeTo(OutputStream)方法中,这会一次返回所有写入到给定流中的内容。
当实体通过一个收到的报文获取时, HttpEntity#getContentType()方法和
HttpEntity#getContentLength() 方 法 可 以 用 来 读 取 通 用 的 元 数 据 , 如
Content-Type 和 Content-Length 头部信息(如果它们是可用的)。因为头部信息
Content-Type 可以包含对文本 MIME 类型的字符编码,比如 text/plain 或 text/html,HttpEntity#getContentEncoding()方法用来读取这个信息。 如果头部信息不可用,那么就返回长度-1,而对于内容类型返回 NULL。如果头部信息 Content-Type 是可用的,那么就会返回一个 Header 对象。
当为一个传出报文创建实体时,这个元数据不得不通过实体创建器来提供。
StringEntity myEntity = new StringEntity("important message","UTF-8");
System.out.println(myEntity.getContentType());
System.out.println(myEntity.getContentLength());
System.out.println(EntityUtils.getContentCharSet(myEntity));
System.out.println(EntityUtils.toString(myEntity));
System.out.println(EntityUtils.toByteArray(myEntity).length);
输出内容为
Content-Type: text/plain; charset=UTF-8
17
UTF-8
important message
17
也可能会有特殊情况,当整个响应内容的一小部分需要获取,消耗剩余内容而损失性能,还有重用连接的代价太高,则可以仅仅通过调用 HttpUriRequest#abort()方法来中止请求。
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int byteOne = instream.read();
int byteTwo = instream.read();
// Do not need the rest
httpget.abort();
}
HttpClient 也自带 EntityUtils 类,这会暴露出一些静态方法,这些方法可以更加容易地从实体中读取内容或信息。代替直接读取 java.io.InputStream, 也可以使用这个类中的方法以字符串/字节数组的形式获取整个内容体。然而, EntityUtils 的使用是强烈不鼓励的,除非响应实体源自可靠的 HTTP服务器和已知的长度限制。
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
long len = entity.getContentLength();
if (len != -1 && len < 2048) {
System.out.println(EntityUtils.toString(entity));
} else {
// Stream content out
}
}
在一些情况下可能会不止一次的读取实体。此时实体内容必须以某种方式在内存或磁盘上被缓冲起来。最简单的方法是通过使用 BufferedHttpEntity 类来包装源实体完成。这会引起源实体内容被读取到内存的缓冲区中。在其它所有方式中,实体包装器将会得到源实体。
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
entity = new BufferedHttpEntity(entity);
}
生成实体内容
HttpClient 提供一些类,它们可以用于生成通过 HTTP 连接获得内容的有效输出流。为了封闭实体从 HTTP 请求中获得的输出内容,那些类的实例可以和封闭如 POST 和 PUT 请求的实体相关联。 HttpClient 为很多公用的数据容器,比如字符串,字节数组,输入流和文件提供了一些类: StringEntity, ByteArrayEntity, InputStreamEntity 和FileEntity。
example:
File file = new File("somefile.txt");
FileEntity entity = new FileEntity(file, "text/plain;
charset=\"UTF-8\"");
HttpPost httppost = new HttpPost("http://localhost/action.do");
httppost.setEntity(entity);
动态内容实体
通常来说 , HTTP 实体 需要基于 特定的 执行上 下文来 动态地生 成。通 过使用
EntityTemplate 实体类和 ContentProducer 接口, HttpClient 提供了动态实体的支
持。 内容生成器是按照需求生成它们内容的对象,将它们写入到一个输出流中。 它们是每次被请求时来生成内容。所以用 EntityTemplate 创建的实体通常是自我包含而且可以重复的。
ContentProducer cp = new ContentProducer() {
public void writeTo(OutputStream outstream) throws IOException {
Writer writer = new OutputStreamWriter(outstream, "UTF-8");
writer.write("<response>");
writer.write(" <content>");
writer.write(" important stuff");
writer.write(" </content>");
writer.write("</response>");
writer.flush();
}
};
HttpEntity entity = new EntityTemplate(cp);
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);
HTML 表单:
许多应用程序需要频繁模拟提交一个 HTML 表单的过程,比如,为了来记录一个 Web应用程序或提交输出数据。 HttpClient 提供了特殊的实体类 UrlEncodedFormEntity 来这个满足过程。
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("param1", "value1"));
formparams.add(new BasicNameValuePair("param2", "value2"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);
param1=value1¶m2=value2
内容分块
我们推荐让 HttpClient 选择基于被传递的 HTTP 报文属性的最适合的编码转换。
这是可能的,但是,设置 HttpEntity#setChunked()方法为 true 是通知 HttpClient 分
块编码的首选。 请注意 HttpClient 将会使用标识作为提示。当使用的 HTTP 协议版本,如HTTP/1.0 版本,不支持分块编码时,这个值会被忽略。
StringEntity entity = new StringEntity("important message","text/plain; charset=\"UTF-8\"");
entity.setChunked(true);
HttpPost httppost = new
HttpPost("http://localhost/acrtion.do");
httppost.setEntity(entity);
响应控制器:
控制响应的最简便和最方便的方式是使用 ResponseHandler 接口。这个放完完全减
轻了用户关于连接管理的担心。当使用 ResponseHandler 时, HttpClient 将会自动关注并保证释放连接到连接管理器中去,而不管请求执行是否成功或引发了异常。
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");
ResponseHandler<byte[]> handler = new ResponseHandler<byte[]>() {
public byte[] handleResponse(HttpResponse response) throws ClientProtocolException,
IOException {
HttpEntity entity = response.getEntity();
if (entity != null) {
return EntityUtils.toByteArray(entity);
} else {
return null;
}
}
};
byte[] response = httpclient.execute(httpget, handler);
HTTP 执行的环境:
HTTP是被设计成无状态的,面向请求-响应的协议。然而,真实的应用程序经常
需要通过一些逻辑相关的请求-响应交换来持久状态信息。 为了开启应用程序来维持一个过程状态, HttpClient允许HTTP请求在一个特定的执行环境中来执行, 简称为HTTP上下文。
HttpClient添加了下列属性到执行上下文中:
? 'http.connection': HttpConnection实例代表了连接到目标服务器的真实连接。
? 'http.target_host': HttpHost实例代表了连接目标。
? 'http.proxy_host': 如果使用了, HttpHost实例代表了代理连接。
? 'http.request': HttpRequest 实例代表了真实的 HTTP 请求。
? 'http.response': HttpResponse 实例代表了真实的 HTTP 响应。
? 'http.request_sent': java.lang.Boolean 对象代表了暗示真实请求是否被完
全传送到目标连接的标识。
比如,为了决定最终的重定向目标,在请求执行之后,可以检查 http.target_host
属性的值:
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://www.google.com/");
HttpResponse response = httpclient.execute(httpget,
localContext);
HttpHost target = (HttpHost) localContext.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
System.out.println("Final target: " + target);
HttpEntity entity = response.getEntity();
if (entity != null) {
entity.consumeContent();
}
输出内容是:
Final target: http://www.google.ch
请求重试处理:
为了开启自定义异常恢复机制,应该提供一个 HttpRequestRetryHandler 接口的
实现。
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(IOException exception,
int executionCount,HttpContext context) {
if (executionCount >= 5) {
// 如果超过最大重试次数,那么就不要继续了
return false;
}
if (exception instanceof NoHttpResponseException) {
// 如果服务器丢掉了连接,那么就重试
return true;
}
if (exception instanceof SSLHandshakeException) {
// 不要重试SSL握手异常
return false;
}
HttpRequest request = (HttpRequest) context.getAttribute(
ExecutionContext.HTTP_REQUEST);
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
if (idempotent) {
// 如果请求被认为是幂等的,那么就重试
return true;
}
return false;
}
};
httpclient.setHttpRequestRetryHandler(myRetryHandler);
HTTP 参数:
HttpParams 接口代表了定义组件运行时行为的一个不变的值的集合。很多情况下,
HttpParams 和 HttpContext 相似。二者之间的主要区别是它们在运行时使用的不同。这两个接口表示了对象的集合,它们被视作为访问对象值的键的 Map,但是服务于不同的目的:
? HttpParams 旨在包含简单对象:整型,浮点型,字符串,集合,还有运行时不变的对象。
? HttpParams 希望被用在“一次写入-多处准备”模式下。 HttpContext 旨在包含很可能在 HTTP 报文处理这一过程中发生改变的复杂对象
? HttpParams 的目标是定义其它组件的行为。通常每一个复杂的组件都有它自己的HttpParams 对象。 HttpContext 的目标是来表示一个 HTTP 处理的执行状态。通
常相同的执行上下文在很多合作的对象中共享。
在 HTTP 请求执行过程中, HttpRequest 对象的 HttpParams 是和用于执行请求的
HttpClient 实例的 HttpParams 联系在一起的。这使得设置在 HTTP 请求级别的参数优先于设置在 HTTP客户端级别的HttpParams。推荐的做法是设置普通参数对所有的在 HTTP客户端级别的 HTTP 请求共享,而且可以选择性重写具体在 HTTP 请求级别的参数。
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getParams().setParameter(CoreProtocolPNames.PROTO
COL_VERSION,HttpVersion.HTTP_1_0);
httpclient.getParams().setParameter(CoreProtocolPNames.HTTP_
CONTENT_CHARSET,"UTF-8");
HttpGet httpget = new HttpGet("http://www.google.com/");
httpget.getParams().setParameter(CoreProtocolPNames.PROTOCOL
_VERSION,HttpVersion.HTTP_1_1);
httpget.getParams().setParameter(CoreProtocolPNames.USE_EXPE
CT_CONTINUE,Boolean.FALSE);
httpclient.addRequestInterceptor(new
HttpRequestInterceptor() {
public void process(final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
System.out.println(request.getParams().getParameter(
CoreProtocolPNames.PROTOCOL_VERSION));
System.out.println(request.getParams().getParameter(
CoreProtocolPNames.HTTP_CONTENT_CHARSET));
System.out.println(request.getParams().getParameter(
CoreProtocolPNames.USE_EXPECT_CONTINUE));
System.out.println(request.getParams().getParameter(
CoreProtocolPNames.STRICT_TRANSFER_ENCODING));
}
});
输出内容为:
HTTP/1.1
UTF-8
false
null
HTTP 参数 bean:
HttpParams 接口允许在处理组件的配置上很大的灵活性。很重要的是,新的参数可
以被引入而不会影响老版本的二进制兼容性。然而,和常规的 Java bean 相比, HttpParams也有一个缺点: HttpParams 不能使用 DI 框架来组合。为了缓解这个限制, HttpClient 包含了一些 bean 类,它们可以用来按顺序使用标准的 Java eban 惯例初始化 HttpParams 对象。
HttpParams params = new BasicHttpParams();
HttpProtocolParamBean paramsBean = new
HttpProtocolParamBean(params);
paramsBean.setVersion(HttpVersion.HTTP_1_1);
paramsBean.setContentCharset("UTF-8");
paramsBean.setUseExpectContinue(true);
System.out.println(params.getParameter(
CoreProtocolPNames.PROTOCOL_VERSION));
System.out.println(params.getParameter(
CoreProtocolPNames.HTTP_CONTENT_CHARSET));
System.out.println(params.getParameter(
CoreProtocolPNames.USE_EXPECT_CONTINUE));
System.out.println(params.getParameter(
CoreProtocolPNames.USER_AGENT));
HTTP 请求执行参数
这些参数会影响到请求执行的过程:
? 'http.protocol.version':如果没有在请求对象中设置明确的版本信息,它就定义了
使用的 HTTP 协议版本。这个参数期望得到一个 ProtocolVersion 类型的值。
如果这个参数没有被设置,那么就使用 HTTP/1.1。
? 'http.protocol.element-charset': 定义了编码 HTTP 协议元素的字符集。这个参数期望得到一个 java.lang.String 类型的值。如果这个参数没有被设置,那么就
使用 US-ASCII。 'http.protocol.eontent-charset': 定义了为每个内容主体编码的默认字符集。这个参数期望得到一个 java.lang.String 类型的值。如果这个参数没有被设置,那么就使用 ISO-8859-1。
? 'http.useragent': 定义了头部信息 User-Agent 的内容。这个参数期望得到一个
java.lang.String 类型的值。如果这个参数没有被设置,那么 HttpClient 将会
为它自动生成一个值。
? 'http.protocol.strict-transfer-encoding': 定义了响应头部信息中是否含有一个非法
的 Transfer-Encoding,都要拒绝掉。
? 'http.protocol.expect-continue' : 为 包 含 方 法 的 实 体 激 活 Expect:
100-Continue 握手。 Expect: 100-Continue 握手的目的是允许客户端使
用请求体发送一个请求信息来决定源服务器是否希望在客户端发送请求体之前得
到这个请求(基于请求头部信息)。 Expect: 100-Continue 握手的使用可以
对需要目标服务器认证的包含请求的实体(比如 POST 和 PUT)导致明显的性能
改善。 Expect: 100-Continue 握手应该谨慎使用,因为它和 HTTP 服务器,
不 支 持 HTTP/1.1 协 议的 代 理使 用会 引 起问 题 。这 个参 数 期望 得到 一 个
java.lang.Boolean 类型的值。如果这个参数没有被设置,那么 HttpClient 将
会试图使用握手。
? 'http.protocol.wait-for-continue': 定义了客户端应该等待 100-Continue 响应最
大的毫秒级时间间隔。这个参数期望得到一个 java.lang.Integer 类型的值。
如果这个参数没有被设置,那么 HttpClient 将会在恢复请求体传输之前为确认等待
3 秒。
=============================================
第二章 连接管理
套接字工厂:
HTTP 连接内部使用 java.net.Socket 对象来处理数据在线路上的传输。它们依赖
SocketFactory 接口来创建,初始化和连接套接字。这会使得 HttpClient 的用户可以提供在运行时指定套接字初始化代码的应用程序。 PlainSocketFactory 是创建和初始化普通的(不加密的)套接字的默认工厂。创建套接字的过程和连接到主机的过程是不成对的,所以套接字在连接操作封锁时可以被关闭。
PlainSocketFactory sf = PlainSocketFactory.getSocketFactory();
Socket socket = sf.createSocket();
HttpParams params = new BasicHttpParams();
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,1000L);
sf.connectSocket(socket, "locahost", 8080, null, -1, params);
安全套接字分层:
LayeredSocketFactory 是 SocketFactory 接口的扩展。 分层的套接字工厂可
以创建在已经存在的普通套接字之上的分层套接字。套接字分层主要通过代理来创建安全的套接字。 HttpClient 附带实现了 SSL/TLS 分层的 SSLSocketFactory。 请注意 HttpClient 不使用任何自定义加密功能。它完全依赖于标准的 Java 密码学( JCE)和安全套接字( JSEE)扩展。
SSL/TLS 的定制
HttpClient 使用 SSLSocketFactory来创建 SSL连接。SSLSocketFactory允许高度定制。
它可以使用 javax.net.ssl.SSLContext 的实例作为参数, 并使用它来创建定制 SSL
连接。
TrustManager easyTrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
// 哦,这很简单!
}
@Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
//哦,这很简单!
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[] { easyTrustManager },
null);
SSLSocketFactory sf = new SSLSocketFactory(sslcontext);
SSLSocket socket = (SSLSocket) sf.createSocket();
socket.setEnabledCipherSuites(new String[]
{ "SSL_RSA_WITH_RC4_128_MD5" });
HttpParams params = new BasicHttpParams();
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,
1000L);
sf.connectSocket(socket, "locahost", 443, null, -1, params);
每一个默认的 HttpClient 使用 BrowserCompatHostnameVerifier 的实现。如果
需要的话,它可以指定不同的主机名验证器实现。
SSLSocketFactory sf = new
SSLSocketFactory(SSLContext.getInstance("TLS"));
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
协议模式
Scheme 类代表了一个协议模式,比如“ http”或“ https”同时包含一些协议属性,比如 默 认 端 口 , 用 来 为 给 定 协 议 创 建 java.net.Socket 实 例 的 套 接 字 工 厂 。SchemeRegistry 类用来维持一组 Scheme,当去通过请求 URI 建立连接时, HttpClient可以从中选择
Scheme http = new Scheme("http",
PlainSocketFactory.getSocketFactory(), 80);
SSLSocketFactory sf = new SSLSocketFactory(SSLContext.getInstance("TLS"));
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
Scheme https = new Scheme("https", sf, 443);
SchemeRegistry sr = new SchemeRegistry();
sr.register(http);
sr.register(https);
HttpClient 代理配置:
告诉 HttpClient 通过代理去连接到目标主机的最简单方式是通过设置默认的代理参数:
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpHost proxy = new HttpHost("someproxy", 8080);
httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
也可以构建 HttpClient 使用标准的 JRE 代理选择器来获得代理信息:
DefaultHttpClient httpclient = new DefaultHttpClient();
ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
httpclient.getConnectionManager().getSchemeRegistry(),
ProxySelector.getDefault());
httpclient.setRoutePlanner(routePlanner);
另外一种选择,可以提供一个定制的 RoutePlanner 实现来获得 HTTP 路由计算处理上的复杂的控制:
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.setRoutePlanner(new HttpRoutePlanner() {
public HttpRoute determineRoute(HttpHost target, HttpRequest request,
HttpContext context) throws HttpException {
return new HttpRoute(target, null, new HttpHost("someproxy", 8080),
"https".equalsIgnoreCase(target.getSchemeName()));
}
});
HTTP 连接管理器:
连接操作器
连接操作是客户端的低层套接字或可以通过外部实体,通常称为连接操作的被操作的状态的连接。 OperatedClientConnection 接口扩展了 HttpClientConnection 接
口而且定义了额外的控制连接套接字的方法。 ClientConnectionOperator 接口代表
了创建实例和更新那些对象低层套接字的策略。 实现类最有可能利用 SocketFactory 来创建 java.net.Socket 实例。 ClientConnectionOperator 接口可以让 HttpClient
的用户提供一个连接操作的定制策略和提供可选实现 OperatedClientConnection 接
口的能力。
HTTP 连接是复杂的,有状态的,线程不安全的对象需要正确的管理以便正确地执行功
能。 HTTP 连接在同一时间仅仅只能由一个执行线程来使用。 HttpClient 采用一个特殊实体来
管理访问 HTTP 连接,这被称为 HTTP 连接管理器,代表了 ClientConnectionManager
接口。一个 HTTP 连接管理器的目的是作为工厂服务于新的 HTTP 连接,管理持久连接和同
步访问持久连接来确保同一时间仅有一个线程可以访问一个连接。
内部的 HTTP 连接管理器和 OperatedClientConnection 实例一起工作,但是它
们 为 服 务 消 耗 器 ManagedClientConnection 提 供 实 例 。
ManagedClientConnection 扮 演 连 接 之 上 管 理 状 态 控 制 所 有 I/O 操 作 的
OperatedClientConnection 实例的包装器。 它也抽象套接字操作,提供打开和更新
去创建路由套接字便利的方法。 ManagedClientConnection 实例了解产生它们到连接
管理器的链接,而且基于这个事实,当不再被使用时,它们必须返回到管理器。
ManagedClientConnection 类也实现了 ConnectionReleaseTrigger 接口,可以被用
来触发释 放连 接返回 给管理 器。 一 旦释 放连接 操作被 触发了 ,被 包装的 连接从
ManagedClientConnection 包装器中脱离, OperatedClientConnection 实例
被返回给管理器。 尽管服务消耗器仍然持有 ManagedClientConnection 实例的引用,
它也不再去执行任何 I/O操作或有意无意地改变的OperatedClientConnection状态。
这里有一个从连接管理器中获取连接的示例:
HttpParams params = new BasicHttpParams();
Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
SchemeRegistry sr = new SchemeRegistry();
sr.register(http);
ClientConnectionManager connMrg = new SingleClientConnManager(params, sr);
// 请求新连接。这可能是一个很长的过程。
ClientConnectionRequest connRequest =
connMrg.requestConnection( new HttpRoute(new HttpHost("localhost", 80)), null);
// 等待连接10秒
ManagedClientConnection conn = connRequest.getConnection(10,TimeUnit.SECONDS);
try {
// 用连接在做有用的事情。当完成时释放连接。
conn.releaseConnection();
} catch (IOException ex) {
// 在I/O error之上终止连接。
conn.abortConnection();
throw ex;
如 果 需 要 , 连 接 请 求 可 以 通 过 调 用 来
ClientConnectionRequest#abortRequest() 方 法 过 早 地 中 断。 这 会 解 锁 在
ClientConnectionRequest#getConnection()方法中被阻止的线程。
一旦响应内容被完全消耗后, BasicManagedEntity 包装器类可以用来保证自动释
放 低 层 的 连 接 。 HttpClient 内 部 使 用 这 个 机 制 来 实 现 透 明 地 对 所 有 从
HttpClient#execute()方法中获得响应释放连接:
ClientConnectionRequest connRequest = connMrg.requestConnection(
new HttpRoute(new HttpHost("localhost", 80)), null);
ManagedClientConnection conn = connRequest.getConnection(10,TimeUnit.SECONDS);
try {
BasicHttpRequest request = new BasicHttpRequest("GET", "/");
conn.sendRequestHeader(request);
HttpResponse response = conn.receiveResponseHeader();
conn.receiveResponseEntity(response);
HttpEntity entity = response.getEntity();
if (entity != null) {
BasicManagedEntity managedEntity = new
BasicManagedEntity(entity, conn, true);
// 替换实体
response.setEntity(managedEntity);
}
// 使用响应对象做有用的事情。当响应内容被消耗后这个连接将会自动释放。
} catch (IOException ex) {
//在I/O error之上终止连接。
conn.abortConnection();
throw ex;
简单连接管理器
SingleClientConnManager 是一个简单的连接管理器, 在同一时间它仅仅维护一
个 连 接 。 尽 管 这 个 类 是 线 程 安 全 的 , 但 它 应 该 被 用 于 一 个 执 行 线 程 。
SingleClientConnManager 对于同一路由的后续请求会尽量重用连接。而如果持久连
接的路由不匹配连接请求的话,它也会关闭存在的连接之后对给定路由再打开一个新的。 如
果连接已经被分配,将会抛出 java.lang.IllegalStateException 异常。
对于每个默认连接, HttpClient 使用 SingleClientConnManager。
连接池管理器
ThreadSafeClientConnManager 是一个复杂的实现来管理客户端连接池,它也
可以从多个执行线程中服务连接请求。对每个基本的路由,连接都是池管理的。对于路由的
请求,管理器在池中有可用的持久性连接,将被从池中租赁连接服务,而不是创建一个新的
连接。
ThreadSafeClientConnManager 维护每个基本路由的最大连接限制。每个默认
的实现对每个给定路由将会创建不超过两个的并发连接,而总共也不会超过 20 个连接。对
于很多真实的应用程序,这个限制也证明很大的制约,特别是他们在服务中使用 HTTP 作为
传输协议。连接限制,也可以使用 HTTP 参数来进行调整。
这个示例展示了连接池参数是如何来调整的:
HttpParams params = new BasicHttpParams();
// 增加最大连接到200
ConnManagerParams.setMaxTotalConnections(params, 200);
// 增加每个路由的默认最大连接到20
ConnPerRouteBean connPerRoute = new ConnPerRouteBean(20);
// 对localhost:80增加最大连接到50
HttpHost localhost = new HttpHost("locahost", 80);
connPerRoute.setMaxForRoute(new HttpRoute(localhost), 50);
ConnManagerParams.setMaxConnectionsPerRoute(params,
connPerRoute);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
new Scheme("http", PlainSocketFactory.getSocketFactory(),
80));
schemeRegistry.register(
new Scheme("https", SSLSocketFactory.getSocketFactory(),
443));
ClientConnectionManager cm = new
ThreadSafeClientConnManager(params, schemeRegistry);
HttpClient httpClient = new DefaultHttpClient(cm, params);
连接管理器关闭
当一个 HttpClient 实例不再需要时,而且即将走出使用范围,那么关闭连接管理器来保
证由管理器保持活动的所有连接被关闭,由连接分配的系统资源被释放是很重要的。
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://www.google.com/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
System.out.println(response.getStatusLine());
if (entity != null) {
entity.consumeContent();
}
httpclient.getConnectionManager().shutdown();
多线程执行请求
当配备连接池管理器时,比如 ThreadSafeClientConnManager, HttpClient 可以同时被用
来执行多个请求,使用多线程执行。
ThreadSafeClientConnManager 将会分配基于它的配置的连接。如果对于给定
路由的所有连接都被租出了,那么连接的请求将会阻塞,直到一个连接被释放回连接池。 它
可以通过设置'http.conn-manager.timeout'为一个正数来保证连接管理器不会在
连接请求执行时无限期的被阻塞。 如果连接请求不能在给定的时间周期内被响应,将会抛出
ConnectionPoolTimeoutException 异常。
HttpParams params = new BasicHttpParams();
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
ClientConnectionManager cm = new
ThreadSafeClientConnManager(params, schemeRegistry);
HttpClient httpClient = new DefaultHttpClient(cm, params);
// 执行GET方法的URI
String[] urisToGet = {
"http://www.domain1.com/",
"http://www.domain2.com/",
"http://www.domain3.com/",
"http://www.domain4.com/"
};
// 为每个URI创建一个线程
GetThread[] threads = new GetThread[urisToGet.length];
for (int i = 0; i < threads.length; i++) {
HttpGet httpget = new HttpGet(urisToGet[i]);
threads[i] = new GetThread(httpClient, httpget);
}
// 开始执行线程
for (int j = 0; j < threads.length; j++) {
threads[j].start();
}
// 合并线程
for (int j = 0; j < threads.length; j++) {
threads[j].join();
}
static class GetThread extends Thread {
private final HttpClient httpClient;
private final HttpContext context;
private final HttpGet httpget;
public GetThread(HttpClient httpClient, HttpGet httpget) {
this.httpClient = httpClient;
this.context = new BasicHttpContext();
this.httpget = httpget;
}
@Override
public void run() {
try {
HttpResponse response =
this.httpClient.execute(this.httpget, this.context);
HttpEntity entity = response.getEntity();
if (entity != null) {
// 对实体做些有用的事情...
// 保证连接能释放回管理器
entity.consumeContent();
}
} catch (Exception ex) {
this.httpget.abort();
}
}
}
连接收回策略
一个经典的阻塞 I/O 模型的主要缺点是网络套接字仅当 I/O 操作阻塞时才可以响应 I/O事件。当一个连接被释放返回管理器时,它可以被保持活动状态而却不能监控套接字的状态和响应任何 I/O 事件。 如果连接在服务器端关闭,那么客户端连接也不能去侦测连接状态中的变化和关闭本端的套接字去作出适当响应。HttpClient 通过测试连接是否是过时的来尝试去减轻这个问题,这已经不再有效了,因为它已经在服务器端关闭了,之前使用执行 HTTP 请求的连接。过时的连接检查也并不是 100%的稳定,反而对每次请求执行还要增加 10 到 30 毫秒的开销。唯一可行的而不涉及到每个对空闲连接的套接字模型线程解决方案,是使用专用的监控线程来收回因为长时间不活动而被
认 为 是 过 期 的 连 接 。 监 控 线 程 可 以 周 期 地 调 用
ClientConnectionManager#closeExpiredConnections()方法来关闭所有过
期 的 连 接 , 从 连 接 池 中 收 回 关 闭 的 连 接 。 它 也 可 以 选 择 性 调 用
ClientConnectionManager#closeIdleConnections()方法来关闭所有已经空
闲超过给定时间周期的连接。
public static class IdleConnectionMonitorThread extends Thread {
private final ClientConnectionManager connMgr;
private volatile boolean shutdown;
public IdleConnectionMonitorThread(ClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(5000);
// 关闭过期连接
connMgr.closeExpiredConnections();
// 可选地,关闭空闲超过30秒的连接
connMgr.closeIdleConnections(30,
TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
// 终止
}
}
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
连接保持活动的策略
HTTP 规范没有确定一个持久连接可能或应该保持活动多长时间。一些 HTTP 服务器使用非标准的头部信息Keep-Alive来告诉客户端它们想在服务器端保持连接活动的周期秒数。如果这个信息可用, HttClient 就会利用这个它。如果头部信息 Keep-Alive 在响应中不存在, HttpClient 假设连接无限期的保持活动。然而许多现实中的 HTTP 服务器配置了在特定不活动周期之后丢掉持久连接来保存系统资源,往往这是不通知客户端的。如果默认的策略证明是过于乐观的,那么就会有人想提供一个定制的保持活动策略。
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.setKeepAliveStrategy(new
ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response,HttpContext context) {
// 兑现'keep-alive'头部信息
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
HttpHost target = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
if("www.naughty-server.com".equalsIgnoreCase(target.getHostNa
me())) {
// 只保持活动5秒
return 5 * 1000;
} else {
// 否则保持活动30秒
return 30 * 1000;
}
}
});
第三章 HTTP 状态管理
原始的 HTTP 是被设计为无状态的,面向请求/响应的协议,没有特殊规定有状态的,贯穿一些逻辑相关的请求/响应交换的会话。 由于 HTTP 协议变得越来越普及和受欢迎,越来越多的从前没有打算使用它的系统也开始为应用程序来使用它,比如作为电子商务应用程序的传输方式。因此, 支持状态管理就变得非常必要了。
网景公司,一度成为 Web 客户端和服务器软件开发者的领导方向,在它们基于专有规范的产品中实现了对 HTTP 状态管理的支持。 之后,网景公司试图通过发布规范草案来规范这种机制。 它们的努力通过 RFC 标准跟踪促成了这些规范定义。然而,在很多应用程序中的状态管理仍然基于网景公司的草案而不兼容官方的规范。 很多主要的 Web 浏览器开发者觉得有必要保留那些极大促进标准片段应用程序的兼容性。
3.1 HTTP cookies
Cookie 是 HTTP 代理和目标服务器可以交流保持会话的状态信息的令牌或短包。网景公司的工程师用它来指“魔法小甜饼”和粘住的名字。
HttpClient 使用 Cookie 接口来代表抽象的 cookie 令牌。 在它的简单形式中 HTTP 的cookie 几乎是名/值对。 通常一个 HTTP 的 cookie 也包含一些属性,比如版本号,合法的域名,指定 cookie 应用所在的源服务器 URL 子集的路径, cookie 的最长有效时间。
SetCookie接口代表由源服务器发送给HTTP代理的响应中的头部信息Set-Cookie
来维持一个对话状态。 SetCookie2接口和指定的Set-Cookie2方法扩展了SetCookie。
SetCookie 接口和额外的如获取原始 cookie 属性的能力,就像它们由源服务器指定
的客户端特定功能扩展了 Cookie 接口。这对生成 Cookie 头部很重要,因为一些 cookie规范需要。 Cookie 头部应该包含在 Set-Cookie 或 Set-Cookie2 头部中指定的特定属性。
3.1.1 Cookie 版本
Cookie 兼容网景公司的草案标准,但是版本 0 被认为是不符合官方规范的。符合标准的cookie 的期望版本是 1。 HttpClient 可以处理基于不同版本的 cookie。
这里有一个重新创建网景公司草案 cookie 示例:
BasicClientCookie netscapeCookie = new BasicClientCookie("name", "value");
netscapeCookie.setVersion(0);
netscapeCookie.setDomain(".mycompany.com");
netscapeCookie.setPath("/");
选择 cookie 策略
Cookie 策略可以在 HTTP 客户端被设置,如果需要,在 HTTP 请求级重写。
HttpClient httpclient = new DefaultHttpClient();
// 对每个默认的强制严格cookie策略
httpclient.getParams().setParameter(
ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2965);
HttpGet httpget = new HttpGet("http://www.broken-server.com/");
// 对这个请求覆盖默认策略
httpget.getParams().setParameter( ClientPNames.COOKIE_POLICY,
CookiePolicy.BROWSER_COMPATIBILITY);
定制 cookie 策略
为了实现定制 cookie 策略,我们应该创建 CookieSpec 接口的定制实现类,创建一个CookieSpecFactory 实现来创建和初始化定制实现的实例并和 HttpClient 注册这个工厂。一旦定制实现被注册了,它可以和标准的 cookie 实现有相同的活性。
CookieSpecFactory csf = new CookieSpecFactory() {
public CookieSpec newInstance(HttpParams params) {
return new BrowserCompatSpec() {
@Override
public void validate(Cookie cookie, CookieOrigin origin)
throws MalformedCookieException {
// 这相当简单
}
};
}
};
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getCookieSpecs().register("easy", csf);
httpclient.getParams().setParameter(
ClientPNames.COOKIE_POLICY, "easy");
Cookie 持久化
HttpClient 可以和任意物理表示的实现了CookieStore接口的持久化 cookie存储一起
使 用 。 默 认 的 CookieStore 实 现 称 为 BasicClientCookie , 这 是 凭 借
java.util.ArrayList 的一个简单实现。 在 BasicClientCookie 对象中存储的cookie 当容器对象被垃圾回收机制回收时会丢失。 如果需要,用户可以提供更复杂的实现。
DefaultHttpClient httpclient = new DefaultHttpClient();
// 创建一个本地的cookie store实例
CookieStore cookieStore = new MyCookieStore();
// 如果需要填充cookie
BasicClientCookie cookie = new BasicClientCookie("name","value");
cookie.setVersion(0);
cookie.setDomain(".mycompany.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
// 设置存储
httpclient.setCookieStore(cookieStore);
HTTP 状态管理和执行上下文
在 HTTP 请求执行的过程中,HttpClient 添加了下列和状态管理相关的对象到执行上下文
中:
? 'http.cookiespec-registry': CookieSpecRegistry 实例代表了实际的 cookie 规
范注册表。这个属性的值设置在本地内容中,优先于默认的。
? 'http.cookie-spec': CookieSpec 实例代表真实的 cookie 规范。
? 'http.cookie-origin': CookieOrigin 实例代表了真实的源服务器的详细信息。
? 'http.cookie-store': CookieStore 实例代表了真实的 cookie 存储。 设置在本地
内容中的这个属性的值优先于默认的。
本地的 HttpContext 对象可以被用来定制 HTTP 状态管理内容,先于请求执行或在
请求执行之后检查它的状态:
HttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/");
HttpResponse response = httpclient.execute(httpget, localContext);
CookieOrigin cookieOrigin = (CookieOrigin)
localContext.getAttribute( ClientContext.COOKIE_ORIGIN);
System.out.println("Cookie origin: " + cookieOrigin);
CookieSpec cookieSpec = (CookieSpec) localContext.getAttribute(
ClientContext.COOKIE_SPEC);
System.out.println("Cookie spec used: " + cookieSpec);
每个用户/线程的状态管理
我们可以使用独立的本地执行上下文来实现对每个用户(或每个线程)状态的管理。定义在本地内容中的 cookie 规范注册表和 cookie 存储将会优先于设置在 HTTP 客户端级别中默认的那些。
HttpClient httpclient = new DefaultHttpClient();
// 创建cookie store的本地实例
CookieStore cookieStore = new BasicCookieStore();
// 创建本地的HTTP内容
HttpContext localContext = new BasicHttpContext();
// 绑定定制的cookie store到本地内容中
localContext.setAttribute( ClientContext.COOKIE_STORE, cookieStore);
HttpGet httpget = new HttpGet("http://www.google.com/");
// 作为参数传递本地内容
HttpResponse response = httpclient.execute(httpget, localContext);
第四章 HTTP 认证
4.1 用户凭证
任何用户身份验证的过程都需要一组可以用于建立用户身份的凭据。用户凭证的最简单的形式可以仅仅是用户名/密码对。 UsernamePasswordCredentials 代表了一组包含安全规则和明文密码的凭据。 这个实现对由 HTTP 标准规范中定义的标准认证模式是足够的
UsernamePasswordCredentials creds = new
UsernamePasswordCredentials("user", "pwd");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());
NTCredentials是微软Windows指定的实现,它包含了除了用户名/密码对外,一组
额外的Windows指定的属性,比如用户域名的名字,比如在微软的Windows网络中,相同的用户使用不同设置的认证可以属于不同的域。
NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());
4.2 认证模式
AuthScheme 接口代表了抽象的,面向挑战-响应的认证模式。 一个认证模式期望支持如下的功能:
? 解析和处理由目标服务器在对受保护资源请求的响应中发回的挑战。
? 提供处理挑战的属性:认证模式类型和它的参数,如果可用,比如这个认证模型可应用的领域。
? 对给定的凭证组和 HTTP 请求对响应真实认证挑战生成认证字符串。要注意认证模式可能是有状态的,涉及一系列的挑战-响应交流。 HttpClient 附带了一些
AuthScheme 实现:
? Basic(基本): Basic 认证模式定义在 RFC 2617 中。这个认证模式是不安全的,因为凭据以明文形式传送。尽管它不安全,如果用在和 TLS/SSL 加密的组合中, Basic认证模式是完全够用的。
? Digest(摘要): Digest 认证模式定义在 RFC 2617 中。 Digest 认证模式比 Basic 有显著的安全提升,对不想通过 TLS/SL 加密在完全运输安全上开销的应用程序来说也是很好的选择。
? NTLM: NTLM 是一个由微软开发的优化 Windows 平台的专有认证模式。 NTLM 被认为是比 Digest 更安全的模式。这个模式需要外部的 NTLM 引擎来工作。要获取更多详情请参考包含在 HttpClient 发布包中的 NTLM_SUPPORT.txt 文档。
认证模式注册表
HttpClient 使用 AuthSchemeRegistry 类维护一个可用的认证模式的注册表。 对于
每个默认的下面的模式是注册过的:
? Basic:基本认证模式
? Digest:摘要认证模式
请注意 NTLM 模式没有对每个默认的进行注册。 NTLM 不能对每个默认开启是应为许可和法律上的原因。要获取更详细的关于如何开启 NTLM 支持的内容请看这部分。
凭据提供器
凭据提供器意来维护一组用户凭据,还有能够对特定认证范围生产用户凭据。认证范围包括主机名,端口号,领域名称和认证模式名称。当使用凭据提供器来注册凭据时,我们可以提供一个通配符(任意主机,任意端口,任意领域,任意模式)来替代确定的属性值。 如果直接匹配没有发现,凭据提供器期望被用来发现最匹配的特定范围。
HttpClient 可以和任意实现了CredentialsProvider接口的凭据提供器的物理代表
一 同 工 作 。 默 认 的 CredentialsProvider 实 现 被 称 为
BasicCredentialsProvider,它是简单的凭借 java.util.HashMap 的实现。
CredentialsProvider credsProvider = new
BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("somehost", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("u1", "p1"));
credsProvider.setCredentials(
new AuthScope("somehost", 8080),
new UsernamePasswordCredentials("u2", "p2"));
credsProvider.setCredentials(
new AuthScope("otherhost", 8080, AuthScope.ANY_REALM, "ntlm"),
new UsernamePasswordCredentials("u3", "p3"));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 80, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, null, "ntlm")));
HTTP 认证和执行上下文
HttpClient 依赖于 AuthState 类来跟踪关于认证过程状态的详细信息。在 HTTP 请求
执行过程中, HttpClient 创建 2 个 AuthState 的实例:一个对于目标主机认证,另外一个对于代理认证。 如果目标服务器或代理需要用户认证,那么各自的 AuthState 实例将会被在认证处理过程中使用的 AuthScope, AuthScheme 和 Crednetials 来填充。
AuthState 可以被检查来找出请求的认证是什么类型的,是否匹配 AuthScheme 的实现,是否凭据提供器对给定的认证范围去找用户凭据。
在 HTTP 请求执行的过程中, HttpClient 添加了下列和认证相关的对象到执行上下文中:
'http.authscheme-registry': AuthSchemeRegistry 实例代表真实的认证模式注册表。
在本地内容中设置的这个属性的值优先于默认的。
'http.auth.credentials-provider': CookieSpec 实例代表了真实的凭据提供器。在本地
内容中设置的这个属性的值优先于默认的。
'http.auth.target-scope': AuthState 实例代表了真实的目标认证状态。在本地内容中
设置的这个属性的值优先于默认的。
'http.auth.proxy-scope': AuthState 实例代表了真实的代理认证状态。在本地内容中
设置的这个属性的值优先于默认的。
本地的 HttpContext 对象可以用于定制 HTTP 认证内容,并先于请求执行或在请求
被执行之后检查它的状态:
HttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/");
HttpResponse response = httpclient.execute(httpget, localContext);
AuthState proxyAuthState = (AuthState)localContext.getAttribute(
ClientContext.PROXY_AUTH_STATE);
System.out.println("Proxy auth scope: " +proxyAuthState.getAuthScope());
System.out.println("Proxy auth scheme: " +proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " +proxyAuthState.getCredentials());
AuthState targetAuthState = (AuthState)localContext.getAttribute(
ClientContext.TARGET_AUTH_STATE);
System.out.println("Target auth scope: " +targetAuthState.getAuthScope());
System.out.println("Target auth scheme: " +targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " +targetAuthState.getCredentials());
抢占认证
HttpClient 不支持开箱的抢占认证,因为滥用或重用不正确的抢占认证可能会导致严重的安全问题,比如将用户凭据以明文形式发送给未认证的第三方。因此,用户期望评估抢占认证和在它们只能应用程序环境内容安全风险潜在的好处,而且要求使用如协议拦截器的标准 HttpClient 扩展机制添加对抢占认证的支持。
这是一个简单的协议拦截器, 如果没有企图认证, 来抢先引入 BasicScheme 的实例到执行上下文中。请注意拦截器必须在标准认证拦截器之前加入到协议处理链中。
HttpRequestInterceptor preemptiveAuth = new
HttpRequestInterceptor() {
public void process(final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(
ClientContext.TARGET_AUTH_STATE);
CredentialsProvider credsProvider = (CredentialsProvider)
context.getAttribute(ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
// 如果没有初始化auth模式
if (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(
targetHost.getHostName(),
targetHost.getPort());
// 获得匹配目标主机的凭据
Credentials creds =credsProvider.getCredentials(authScope);
// 如果发现了,抢先生成BasicScheme
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
};
DefaultHttpClient httpclient = new DefaultHttpClient();
// 作为第一个拦截器加入到协议链中
httpclient.addRequestInterceptor(preemptiveAuth, 0);
NTLM 连接持久化
NTLM 认证模式是在计算开销方面昂贵的多的,而且对标准的 Basic 和 Digest 模式
的性能影响也很大。 这很可能是为什么微软选择 NTLM 认证模式为有状态的主要原因之一。
也就是说,一旦认证通过,用户标识是和连接的整个生命周期相关联的。 NTLM 连接的状态特性使得连接持久化非常复杂,对于明显的原因,持久化 NTLM 连接不能被使用不同用户标识的用户重用。 标准的连接管理器附带 HttpClient 是完全能够管理状态连接的。而逻辑相关的,使用同一 session 和执行上下文为了让它们了解到当前的用户标识的请求也是极为重要的。否则,HttpClient 将会终止对每个基于 NTLM 保护资源的 HTTP 请求创建新的 HTTP 连接。
要获取关于有状态的 HTTP 连接的详细讨论,请参考这个部分。
因为 NTLM 连接是有状态的,通常建议使用相对简单的方法触发 NTLM 认证,比如 GET或 HEAD, 而重用相同的连接来执行代价更大的方法,特别是它们包含请求实体,比如 POST或 PUT。
DefaultHttpClient httpclient = new DefaultHttpClient();
NTCredentials creds = new NTCredentials("user", "pwd","myworkstation", "microsoft.com");
httpclient.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);
HttpHost target = new HttpHost("www.microsoft.com", 80,"http");
// 保证相同的内容来用于执行逻辑相关的请求
HttpContext localContext = new BasicHttpContext();
// 首先执行简便的方法。这会触发NTLM认证
HttpGet httpget = new HttpGet("/ntlm-protected/info");
HttpResponse response1 = httpclient.execute(target, httpget,localContext);
HttpEntity entity1 = response1.getEntity();
if (entity1 != null) {
entity1.consumeContent();
}
//之后使用相同的内容(和连接)执行开销大的方法。
HttpPost httppost = new HttpPost("/ntlm-protected/form");
httppost.setEntity(new StringEntity("lots and lots of data"));
HttpResponse response2 = httpclient.execute(target, httppost,localContext);
HttpEntity entity2 = response2.getEntity();
if (entity2 != null) {
entity2.consumeContent();
}
第五章 HTTP 客户端服务
5.1 HttpClient 门面
HttpClient 接口代表了最重要的 HTTP 请求执行的契约。它没有在请求执行处理上
强加限制或特殊细节,而在连接管理,状态管理,认证和处理重定向到具体实现上留下了细节。 这应该使得很容易使用额外的功能,比如响应内容缓存来装饰接口。
DefaultHttpClient 是 HttpClient 接口的默认实现。 这个类扮演了很多特殊用
户程序或策略接口实现负责处理特定 HTTP 协议方面,比如重定向到处理认证或做出关于连接持久化和保持活动的持续时间决定的门面。 这使得用户可以选择使用定制, 具体程序等来替换某些方面默认实现。
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(HttpResponse response,
HttpContext context) {
long keepAlive = super.getKeepAliveDuration(response, context);
if (keepAlive == -1) {
// 如果keep-alive值没有由服务器明确设置,那么保持连接持续5秒。
keepAlive = 5000;
}
return keepAlive;
}
});
HTTP 客户端和执行上下文
DefaultHttpClient 将 HTTP 请求视为不变的对象,也从来不会假定在请求执行期
间改变。相反,它创建了一个原请求对象私有的可变副本,副本的属性可以基于执行上下文来更新。因此,如目标主键和请求 URI 的 final 类型的请求参数可以在请求执行之后,由检查本地 HTTP 上下文来决定。
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/");
HttpResponse response = httpclient.execute(httpget,localContext);
HttpHost target = (HttpHost) localContext.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
HttpUriRequest req = (HttpUriRequest)
localContext.getAttribute(ExecutionContext.HTTP_REQUEST);
System.out.println("Target host: " + target);
System.out.println("Final request URI: " + req.getURI());
System.out.println("Final request method: " + req.getMethod());
第六章 高级主题
6.1 自定义客户端连接
在特定条件下,也许需要来定制 HTTP 报文通过线路传递,越过了可能使用的 HTTP 参数来处理非标准不兼容行为的方式。比如,对于 Web 爬虫,它可能需要强制 HttpClient 接受格式错误的响应头部信息,来抢救报文的内容。
通常插入一个自定义的报文解析器的过程或定制连接实现需要几个步骤:
? 提供一个自定义 LineParser/LineFormatter 接口实现。如果需要,实现报
文解析/格式化逻辑。
class MyLineParser extends BasicLineParser {
@Override
public Header parseHeader(
final CharArrayBuffer buffer) throws ParseException {
try {
return super.parseHeader(buffer);
} catch (ParseException ex) {
// 压制ParseException异常
return new BasicHeader("invalid",
buffer.toString());
}
}
}
提过一个自定义的 OperatedClientConnection 实现。替换需要自定义的默
认请求/响应解析器,请求/响应格式化器。如果需要,实现不同的报文写入/读取
代码。
class MyClientConnection extends DefaultClientConnection {
@Override
protected HttpMessageParser createResponseParser(
final SessionInputBuffer buffer,
final HttpResponseFactory responseFactory,
final HttpParams params) {
return new DefaultResponseParser(buffer,
new MyLineParser(),responseFactory,params);
}
}
为了创建新类的连接,提供一个自定义的 ClientConnectionOperator 接口
实现。如果需要,实现不同的套接字初始化代码。
class MyClientConnectionOperator extends
DefaultClientConnectionOperator {
public MyClientConnectionOperator(
final SchemeRegistry sr) {
super(sr);
}
@Override
public OperatedClientConnection createConnection() {
return new MyClientConnection();
}
}
为了创建新类的连接操作,提供自定义的 ClientConnectionManager 接口实
现。
class MyClientConnManager extends SingleClientConnManager {
public MyClientConnManager(
final HttpParams params,
final SchemeRegistry sr) {
super(params, sr);
}
@Override
protected ClientConnectionOperator
createConnectionOperator(
final SchemeRegistry sr) {
return new MyClientConnectionOperator(sr);
}
}
有状态的 HTTP 连接
HTTP 规范假设 session 状态信息通常是以 HTTP cookie 格式嵌入在 HTTP 报文中的,因此HTTP 连接通常是无状态的,这个假设在现实生活中通常是不对的。 也有一些情况,当 HTTP连接使用特定的用户标识或特定的安全上下文来创建时,因此不能和其它用户共享,只能由该用户重用。 这样的有状态的 HTTP 连接的示例就是 NTLM 认证连接和使用客户端证书认证的 SSL 连接。
用户令牌处理器
HttpClient 依赖 UserTokenHandler 接口来决定给定的执行上下文是否是用户指定
的。如果这个上下文是用户指定的或者如果上下文没有包含任何资源或关于当前用户指定详情而是 null,令牌对象由这个处理器返回,期望唯一地标识当前的用户。 用户令牌将被用来保证用户指定资源不会和其它用户来共享或重用。
如果它可以从给定的执行上下文中来获得, UserTokenHandler 接口的默认实现是
使用主类的一个实例来代表 HTTP 连接的状态对象。UserTokenHandler 将会使用基于如NTLM 或开启的客户端认证 SSL 会话认证模式的用户的主连接。 如果二者都不可用,那么就不会返回令牌。
如果默认的不能满足它们的需要,用户可以提供一个自定义的实现:
用户令牌和执行上下文
在 HTTP 请求执行的过程中,HttpClient 添加了下列和用户标识相关的对象到执行上下文中:
? 'http.user-token': 对象实例代表真实的用户标识,通常期望 Principle 接口的
实例。
我们可以在请求被执行后,通过检查本地 HTTP 上下文的内容,发现是否用于执行请求的连接是有状态的。
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/");
HttpResponse response = httpclient.execute(httpget,localContext);
HttpEntity entity = response.getEntity();
if (entity != null) {
entity.consumeContent();
}
Object userToken = localContext.getAttribute(ClientContext.USER_TOKEN);
System.out.println(userToken);
持久化有状态的连接
请注意带有状态对象的持久化连接仅当请求被执行时,相同状态对象被绑定到执行上下文时可以被重用。 所以,保证相同上下文重用于执行随后的相同用户, 或用户令牌绑定到之前请求执行上下文的 HTTP 请求是很重要的。
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext1 = new BasicHttpContext();
HttpGet httpget1 = new HttpGet("http://localhost:8080/");
HttpResponse response1 = httpclient.execute(httpget1,localContext1);
HttpEntity entity1 = response1.getEntity();
if (entity1 != null) {
entity1.consumeContent();
}
Principal principal = (Principal) localContext1.getAttribute(ClientContext.USER_TOKEN);
HttpContext localContext2 = new BasicHttpContext();
localContext2.setAttribute(ClientContext.USER_TOKEN,principal);
HttpGet httpget2 = new HttpGet("http://localhost:8080/");
HttpResponse response2 = httpclient.execute(httpget2,
localContext2);
HttpEntity entity2 = response2.getEntity();
if (entity2 != null) {
entity2.consumeContent();
}
//=====================自己写的获取隐藏预的 值
package pachong6;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import pachong.Almanac;
public class TestHtml
{
public TestHtml()
{
}
private static void doTask(String url)
{
String html = pickData(url);
// System.out.println(html.toString());
analyzeHtml(html);
// String s2 = analyzeHtml(html);
}
// 爬取网页全部数据 getHtmlEntity
private static String pickData(String url)
{
CloseableHttpClient httpclient = HttpClients.createDefault();
try
{
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpget);
try
{
// 获取响应实体
HttpEntity entity = response.getEntity();
// 打印响应状态
if (entity != null)
{
return EntityUtils.toString(entity);
}
} finally
{
response.close();
}
} catch (ClientProtocolException e)
{
e.printStackTrace();
} catch (ParseException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} finally
{
// 关闭连接,释放资源
try
{
httpclient.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
return null;
}
// 解析网页中的元素与数据,提取出来。
private static void analyzeHtml(String html)
{
org.jsoup.nodes.Document document = Jsoup.parse(html);
List<String> elementIds2 = new ArrayList<String>();
// elementIds2.add("__VIEWSTATE");
List<Element> element2=document.getAllElements();
int i= element2.get(20).toString().length();
System.out.println(element2.get(20).toString().subSequence(47, i-4));
if(document != null){
List<String> elementIds = new ArrayList<String>();
elementIds.add("__VIEWSTATE");
elementIds.add("__VIEWSTATEGENERATOR");
elementIds.add("__EVENTVALIDATION");
elementIds.add("RadioButtonList1_0");
elementIds.add("input");
for(String elementId : elementIds){
Element element = document.getElementById(elementId);
if(element != null){
String elementValue = element.attr("value");
System.out.println(elementValue.toString());
}
}
}
// return b;
}
private static String analyzeHtml(org.jsoup.nodes.Document document,String id){
Element element=document.getElementById(id);
Elements elements=element.getElementsByTag("a");
StringBuffer sb=new StringBuffer();
for (Element e : elements) {
sb.append(e.text()+" ");
}
return sb.toString();
}
public static void main(String[] args)
{
TestHtml.doTask("http://xjgl.mnkjxy.com/");
}
}
//=============自己写的 抓包登录成功实验 ,哈哈哈哈哈哈哈哈呵呵
package pachong7;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import pachong.Almanac;
public class TestHtml
{
public TestHtml()
{
}
public static String downHtml(String url) throws IOException
{
String content = null;
// DefaultHttpClient httpclient = new DefaultHttpClient(); 该语句过时了。
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
{
content = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity); // 关闭内容流
}
// httpclient.getConnectionManager().shutdown(); 该语句过时了
response.close();
httpclient.close();
return content;
}
public static String downHtmlTwoMethod(String url) throws IOException
{
String content = null;
CloseableHttpClient httpclient = HttpClients.createDefault();
BasicResponseHandler handler = new BasicResponseHandler();
HttpGet httpget = new HttpGet(url);
do
{
try
{
content = httpclient.execute(httpget, handler);
} catch (ClientProtocolException e)
{
e.printStackTrace();
System.out.println("retry...");
try
{
Thread.sleep(3000);
} catch (InterruptedException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
} while (content == null);
httpclient.close();
return content;
}
// 有些网站只支持get命令,不支持head命令;当然也可以用head命令访问头部信息
public static void getHeads(String url) throws IOException
{
HttpGet httpget = new HttpGet(url);
CloseableHttpClient httpclient = HttpClients.createDefault();
// 或者 是 CloseableHttpClient httpclient = CompanySite.getClient();
CloseableHttpResponse response = httpclient.execute(httpget);
Header[] s = response.getHeaders("last-modified");
if (s.length == 0)
return;
String lastModified = s[0].getValue();
if (lastModified != null)
{
System.out.println("更新时间:" + lastModified);
}
httpclient.close();
}
// 获取页面的编码
public static void getHtmlCharset(String url) throws IOException
{
CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response=null;
String charset = "utf-8";
try{
HttpGet httpget = new HttpGet(url);
response = httpclient.execute(httpget);
Pattern pattern = Pattern.compile("text/html;[\\s]*charset=(.*)");
Header[] arr = response.getHeaders("Content-Type");
if(arr !=null || arr.length !=0 )
{
String content = arr[0].getValue().toLowerCase();
Matcher m = pattern.matcher(content);
if(m.find()){
charset = m.group(1);
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
response.close();
httpclient.close();
}
System.out.println(charset);
//return charset;
}
//
private static String doTask(String url)
{
String html = pickData(url);
// System.out.println(html.toString());
String s= analyzeHtml(html);
// String s2 = analyzeHtml(html);
return s;
}
// 爬取网页全部数据 getHtmlEntity
private static String pickData(String url)
{
CloseableHttpClient httpclient = HttpClients.createDefault();
try
{
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpget);
try
{
// 获取响应实体
HttpEntity entity = response.getEntity();
// 打印响应状态
if (entity != null)
{
return EntityUtils.toString(entity);
}
} finally
{
response.close();
}
} catch (ClientProtocolException e)
{
e.printStackTrace();
} catch (ParseException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} finally
{
// 关闭连接,释放资源
try
{
httpclient.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
return null;
}
// 解析网页中的元素与数据,提取出来。
private static String analyzeHtml(String html)
{
org.jsoup.nodes.Document document = Jsoup.parse(html);
List<String> elementIds2 = new ArrayList<String>();
// elementIds2.add("__VIEWSTATE");
List<Element> element2=document.getAllElements();
int i= element2.get(20).toString().length();
// System.out.println(element2.get(46).toString().subSequence(41, i+4));
String s = element2.get(46).toString().subSequence(41, i+4).toString();
return s;
// System.out.println(element2.get(46).toString());
// return b;
}
///
public static void PostHtml(String geturl,String posturl) throws IOException
{
String html=null;
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(geturl);
CloseableHttpResponse response = httpclient.execute(httpget);
// 获取响应实体
HttpEntity entity = response.getEntity();
// 打印响应状态
if (entity != null)
{
html = EntityUtils.toString(entity);
}
org.jsoup.nodes.Document document = Jsoup.parse(html);
List<String> elementIds2 = new ArrayList<String>();
List<Element> element2=document.getAllElements();
int i= element2.get(20).toString().length();
// System.out.println(element2.get(46).toString().subSequence(41, i+4));
String s = element2.get(46).toString().subSequence(41, i+4).toString();
HttpPost httppost = new HttpPost(posturl);
// CloseableHttpResponse response=null;
// 添加POST参数
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("_csrf", s));
nvps.add(new BasicNameValuePair("LoginForm[username]", "用户名"));
nvps.add(new BasicNameValuePair("LoginForm[password]", "密码"));
nvps.add(new BasicNameValuePair("LoginForm[rememberMe]", "0"));
nvps.add(new BasicNameValuePair("login-button", "登 录 "));
// nvps.add(new BasicNameValuePair("password","admin"));
// httppost.setEntity(new UrlEncodedFormEntity(nvps));
httppost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
// httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
try
{
response = httpclient.execute(httppost);
System.out.println(" response.getStatusLine()"+ response.getStatusLine());
Header locationHeader = response.getLastHeader("Location");
System.out.println("response.getLastHeaderLocation"+locationHeader);
Header locationHeader2 = response.getFirstHeader("Location");
System.out.println("Location"+locationHeader2);
// if(locationHeader2 == null)
// {return;}
// return ;
// String Rurl = locationHeader2.getValue();
// nnnnnnnnnnnnnnnnnnnnnnnn;
httpget = new HttpGet("http://home.51cto.com/space?uid=11535358");
// httpget = new HttpGet(Rurl);
BasicResponseHandler handler = new BasicResponseHandler();
// ResponseHandler<String> handler = new BasicResponseHandler();
String responseBody = httpclient.execute(httpget, handler);
System.out.println("response: " + responseBody);
} catch (ClientProtocolException e)
{
e.printStackTrace();
System.out.println("retry...");
}finally{
response.close();
httpclient.close();
}
}
public static void main(String[] args) throws IOException
{
TestHtml.PostHtml("http://home.51cto.com/index","http://home.51cto.com/index");
}
}
//
/
/
//
//
///
///
///
//=============自己写的 抓包登录成功实验 ,哈哈哈哈哈哈哈哈呵呵===22222
package pachong7;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import pachong.Almanac;
public class TestHtml
{
public TestHtml()
{
}
public static String downHtml(String url) throws IOException
{
String content = null;
// DefaultHttpClient httpclient = new DefaultHttpClient(); 该语句过时了。
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
{
content = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity); // 关闭内容流
}
// httpclient.getConnectionManager().shutdown(); 该语句过时了
response.close();
httpclient.close();
return content;
}
public static String downHtmlTwoMethod(String url) throws IOException
{
String content = null;
CloseableHttpClient httpclient = HttpClients.createDefault();
BasicResponseHandler handler = new BasicResponseHandler();
HttpGet httpget = new HttpGet(url);
do
{
try
{
content = httpclient.execute(httpget, handler);
} catch (ClientProtocolException e)
{
e.printStackTrace();
System.out.println("retry...");
try
{
Thread.sleep(3000);
} catch (InterruptedException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
} while (content == null);
httpclient.close();
return content;
}
// 有些网站只支持get命令,不支持head命令;当然也可以用head命令访问头部信息
public static void getHeads(String url) throws IOException
{
HttpGet httpget = new HttpGet(url);
CloseableHttpClient httpclient = HttpClients.createDefault();
// 或者 是 CloseableHttpClient httpclient = CompanySite.getClient();
CloseableHttpResponse response = httpclient.execute(httpget);
Header[] s = response.getHeaders("last-modified");
if (s.length == 0)
return;
String lastModified = s[0].getValue();
if (lastModified != null)
{
System.out.println("更新时间:" + lastModified);
}
httpclient.close();
}
// 获取页面的编码
public static void getHtmlCharset(String url) throws IOException
{
CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response=null;
String charset = "utf-8";
try{
HttpGet httpget = new HttpGet(url);
response = httpclient.execute(httpget);
Pattern pattern = Pattern.compile("text/html;[\\s]*charset=(.*)");
Header[] arr = response.getHeaders("Content-Type");
if(arr !=null || arr.length !=0 )
{
String content = arr[0].getValue().toLowerCase();
Matcher m = pattern.matcher(content);
if(m.find()){
charset = m.group(1);
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
response.close();
httpclient.close();
}
System.out.println(charset);
//return charset;
}
//
private static String doTask(String url)
{
String html = pickData(url);
// System.out.println(html.toString());
String s= analyzeHtml(html);
// String s2 = analyzeHtml(html);
return s;
}
// 爬取网页全部数据 getHtmlEntity
private static String pickData(String url)
{
CloseableHttpClient httpclient = HttpClients.createDefault();
try
{
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpget);
try
{
// 获取响应实体
HttpEntity entity = response.getEntity();
// 打印响应状态
if (entity != null)
{
return EntityUtils.toString(entity);
}
} finally
{
response.close();
}
} catch (ClientProtocolException e)
{
e.printStackTrace();
} catch (ParseException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} finally
{
// 关闭连接,释放资源
try
{
httpclient.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
return null;
}
// 解析网页中的元素与数据,提取出来。
private static String analyzeHtml(String html)
{
org.jsoup.nodes.Document document = Jsoup.parse(html);
List<String> elementIds2 = new ArrayList<String>();
// elementIds2.add("__VIEWSTATE");
List<Element> element2=document.getAllElements();
int i= element2.get(20).toString().length();
// System.out.println(element2.get(46).toString().subSequence(41, i+4));
String s = element2.get(46).toString().subSequence(41, i+4).toString();
return s;
// System.out.println(element2.get(46).toString());
// return b;
}
///
public static void PostHtml(String geturl,String posturl) throws IOException
{
CookieStore cookieStore = new BasicCookieStore();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build();
String html=null;
// CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(geturl);
CloseableHttpResponse response = httpclient.execute(httpget);
// 获取响应实体
HttpEntity entity = response.getEntity();
// 打印响应状态
if (entity != null)
{
html = EntityUtils.toString(entity);
}
org.jsoup.nodes.Document document = Jsoup.parse(html);
List<String> elementIds2 = new ArrayList<String>();
List<Element> element2=document.getAllElements();
int i= element2.get(20).toString().length();
// System.out.println(element2.get(46).toString().subSequence(41, i+4));
String s = element2.get(46).toString().subSequence(41, i+4).toString();
HttpPost httppost = new HttpPost(posturl);
// CloseableHttpResponse response=null;
// 添加POST参数
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("_csrf", s));
nvps.add(new BasicNameValuePair("LoginForm[username]", "***"));
nvps.add(new BasicNameValuePair("LoginForm[password]", "***"));
nvps.add(new BasicNameValuePair("LoginForm[rememberMe]", "0"));
nvps.add(new BasicNameValuePair("login-button", "登 录 "));
// nvps.add(new BasicNameValuePair("password","admin"));
// httppost.setEntity(new UrlEncodedFormEntity(nvps));
httppost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
// httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
try
{
response = httpclient.execute(httppost);
System.out.println("post logo cookies:");
List<Cookie> cookies = cookieStore.getCookies();
for (int j = 0; j < cookies.size(); j++) {
System.out.println("Local cookie: " + cookies.get(j));
}
//post 设置cokkie 代码可用
// BasicClientCookie cookie = new BasicClientCookie("name", "zhaoke");
// cookie.setVersion(0);
// cookie.setDomain("/pms/"); //设置范围
// cookie.setPath("/");
// cookieStore.addCookie(cookie);
// httpClient.execute(post);//
// List<Cookie> cookies = cookieStore.getCookies();
// for (int i = 0; i < cookies.size(); i++) {
// System.out.println("Local cookie: " + cookies.get(i));
// }
System.out.println(" response.getStatusLine()"+ response.getStatusLine());
Header locationHeader = response.getLastHeader("Location");
System.out.println("response.getLastHeaderLocation"+locationHeader);
Header locationHeader2 = response.getFirstHeader("Location");
System.out.println("Location"+locationHeader2);
// if(locationHeader2 == null)
// {return;}
// return ;
// String Rurl = locationHeader2.getValue();
// nnnnnnnnnnnnnnnnnnnnnnnn;
httpget = new HttpGet("http://home.51cto.com/space?uid=11535358");
// httpget = new HttpGet("http://down.51cto.com/11535358/down");
// httpget = new HttpGet(Rurl);
BasicResponseHandler handler = new BasicResponseHandler();
// ResponseHandler<String> handler = new BasicResponseHandler();
String responseBody = httpclient.execute(httpget, handler);
System.out.println("response: " + responseBody);
} catch (ClientProtocolException e)
{
e.printStackTrace();
System.out.println("retry...");
}finally{
response.close();
httpclient.close();
}
}
public static void main(String[] args) throws IOException
{
TestHtml.PostHtml("http://home.51cto.com/index","http://home.51cto.com/index");
}
}
====================================================
=============================================
http://blog.csdn.net/shiyu_sy/article/details/52449577
http://doc.okbase.net/314649444/archive/77881.html 好的
http://blog.csdn.net/huo_chai_gun/article/details/41516013
http://blog.csdn.net/huo_chai_gun/article/details/41516013
http://blog.csdn.net/zmx729618/article/details/51801958 好的
https://segmentfault.com/a/1190000003013451
http://blog.csdn.net/huo_chai_gun/article/details/41516013
http://blog.csdn.net/blue_jjw/article/details/8768624 头部设置
http://www.cnblogs.com/joeman/archive/2013/03/27/2984490.html
=============================================
http://www.cnblogs.com/amosli/category/589695.html
两图的区别一个是带验证码,一个是不带验证码,下面将先解决不带验证码的登录.
复制代码
package com.amos;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
* @author amosli
* 登录并抓取中国联通数据
*/
public class LoginChinaUnicom {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
String name = "中国联通手机号码";
String pwd = "手机服务密码";
String url = "https://uac.10010.com/portal/Service/MallLogin?callback=jQuery17202691898950318097_1403425938090&redirectURL=http%3A%2F%2Fwww.10010.com&userName=" + name + "&password=" + pwd + "&pwdType=01&productType=01&redirectType=01&rememberMe=1";
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse loginResponse = httpClient.execute(httpGet);
if (loginResponse.getStatusLine().getStatusCode() == 200) {
for (Header head : loginResponse.getAllHeaders()) {
System.out.println(head);
}
HttpEntity loginEntity = loginResponse.getEntity();
String loginEntityContent = EntityUtils.toString(loginEntity);
System.out.println("登录状态:" + loginEntityContent);
//如果登录成功
if (loginEntityContent.contains("resultCode:\"0000\"")) {
//月份
String months[] = new String[]{"201401", "201402", "201403", "201404", "201405"};
for (String month : months) {
String billurl = "http://iservice.10010.com/ehallService/static/historyBiil/execute/YH102010002/QUERY_YH102010002.processData/QueryYH102010002_Data/" + month + "/undefined";
HttpPost httpPost = new HttpPost(billurl);
HttpResponse billresponse = httpClient.execute(httpPost);
if (billresponse.getStatusLine().getStatusCode() == 200) {
saveToLocal(billresponse.getEntity(), "chinaunicom.bill." + month + ".2.html");
}
}
}
}
}
复制代码
找到要登录的url以及要传的参数,这里手机号码服务密码这里就不提供了.
new一个DefaultHttpClient,然后使用Get方式发出请求,如果登录成功,其返回代码是0000.
再用HttpPost方式将返回值写到本地.
复制代码
/**
* 写文件到本地
*
* @param httpEntity
* @param filename
*/
public static void saveToLocal(HttpEntity httpEntity, String filename) {
try {
File dir = new File("/home/amosli/workspace/chinaunicom/");
if (!dir.isDirectory()) {
dir.mkdir();
}
File file = new File(dir.getAbsolutePath() + "/" + filename);
FileOutputStream fileOutputStream = new FileOutputStream(file);
InputStream inputStream = httpEntity.getContent();
if (!file.exists()) {
file.createNewFile();
}
byte[] bytes = new byte[1024];
int length = 0;
while ((length = inputStream.read(bytes)) > 0) {
fileOutputStream.write(bytes, 0, length);
}
inputStream.close();
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
这里如果只是想输出一下可以使用EntityUtils.toString(HttpEntity entity)方法,其源码如下:
复制代码
public static String toString(
final HttpEntity entity, final Charset defaultCharset) throws IOException, ParseException {
Args.notNull(entity, "Entity");
final InputStream instream = entity.getContent();
if (instream == null) {
return null;
}
try {
Args.check(entity.getContentLength() <= Integer.MAX_VALUE,
"HTTP entity too large to be buffered in memory");
int i = (int)entity.getContentLength();
if (i < 0) {
i = 4096;
}
Charset charset = null;
try {
final ContentType contentType = ContentType.get(entity);
if (contentType != null) {
charset = contentType.getCharset();
}
} catch (final UnsupportedCharsetException ex) {
throw new UnsupportedEncodingException(ex.getMessage());
}
if (charset == null) {
charset = defaultCharset;
}
if (charset == null) {
charset = HTTP.DEF_CONTENT_CHARSET;
}
final Reader reader = new InputStreamReader(instream, charset);
final CharArrayBuffer buffer = new CharArrayBuffer(i);
final char[] tmp = new char[1024];
int l;
while((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
return buffer.toString();
} finally {
instream.close();
}
}
复制代码
这里可以发现其实现方式还是比较容易看懂的,可以指定编码,也可以不指定.
2.带验证码的登录,抓取基本信息
复制代码
package com.amos;
import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.*;
import org.apache.http.util.EntityUtils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Created by amosli on 14-6-22.
*/
public class LoginWithCaptcha {
public static void main(String args[]) throws Exception {
//生成验证码的链接
String createCaptchaUrl = "http://uac.10010.com/portal/Service/CreateImage";
HttpClient httpClient = new DefaultHttpClient();
String name = "中国联通手机号码";
String pwd = "手机服务密码";
//这里可自定义所需要的cookie
CookieStore cookieStore = new BasicCookieStore();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build();
//get captcha,获取验证码
HttpGet captchaHttpGet = new HttpGet(createCaptchaUrl);
HttpResponse capthcaResponse = httpClient.execute(captchaHttpGet);
if (capthcaResponse.getStatusLine().getStatusCode() == 200) {
//将验证码写入本地
LoginChinaUnicom.saveToLocal(capthcaResponse.getEntity(), "chinaunicom.capthca." + System.currentTimeMillis());
}
//手工输入验证码并验证
HttpResponse verifyResponse = null;
String capthca = null;
String uvc = null;
do {
//输入验证码,读入键盘输入
//1)
InputStream inputStream = System.in;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
System.out.println("请输入验证码:");
capthca = bufferedReader.readLine();
//2)
//Scanner scanner = new Scanner(System.in);
//capthca = scanner.next();
String verifyCaptchaUrl = "http://uac.10010.com/portal/Service/CtaIdyChk?verifyCode=" + capthca + "&verifyType=1";
HttpGet verifyCapthcaGet = new HttpGet(verifyCaptchaUrl);
verifyResponse = httpClient.execute(verifyCapthcaGet);
AbstractHttpClient abstractHttpClient = (AbstractHttpClient) httpClient;
for (Cookie cookie : abstractHttpClient.getCookieStore().getCookies()) {
System.out.println(cookie.getName() + ":" + cookie.getValue());
if (cookie.getName().equals("uacverifykey")) {
uvc = cookie.getValue();
}
}
} while (!EntityUtils.toString(verifyResponse.getEntity()).contains("true"));
//登录
String loginurl = "https://uac.10010.com/portal/Service/MallLogin?userName=" + name + "&password=" + pwd + "&pwdType=01&productType=01&verifyCode=" + capthca + "&redirectType=03&uvc=" + uvc;
HttpGet loginGet = new HttpGet(loginurl);
CloseableHttpResponse loginResponse = httpclient.execute(loginGet);
System.out.print("loginResponse:" + EntityUtils.toString(loginResponse.getEntity()));
//抓取基本信息数据
HttpPost basicHttpGet = new HttpPost("http://iservice.10010.com/ehallService/static/acctBalance/execute/YH102010005/QUERY_AcctBalance.processData/Result");
LoginChinaUnicom.saveToLocal(httpclient.execute(basicHttpGet).getEntity(), "chinaunicom.basic.html");
}
}
复制代码
这里有两个难点,一是验证码,二uvc码;
验证码,这里将其写到本地,然后人工输入,这个还比较好解决.
uvc码,很重要,这个是在cookie里的,httpclient操作cookie的方法网上找了很久都没有找到,后来看其源码才看到.
3.效果图
账单数据(这里是json格式的数据,可能不太方便查看):
=============================================
早期参考的代码:
=============================================
早期参考的代码:
Java通过httpclient获取cookie模拟登录
?
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 package Step1;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
/**
*
* @ClassName: HttpLogin
* @Description: java通过httpclient获取cookie模拟登录
* @author zeze
* @date 2015年11月10日 下午4:18:08
*
*/
public class HttpLogin {
public static void main(String[] args) {
// 登陆 Url
String loginUrl = "http://passport.mop.com/?targetUrl=http://hi.mop.com/?&g=1447141423230&loginCheck=UNLOGINED";
// 需登陆后访问的 Url
String dataUrl = "http://hi.mop.com/?";
HttpClient httpClient = new HttpClient();
// 模拟登陆,按实际服务器端要求选用 Post 或 Get 请求方式
PostMethod postMethod = new PostMethod(loginUrl);
// 设置登陆时要求的信息,用户名和密码
NameValuePair[] data = { new NameValuePair("loginName", "chzeze123"), new NameValuePair("loginPasswd", "**") };
postMethod.setRequestBody(data);
try {
// 设置 HttpClient 接收 Cookie,用与浏览器一样的策略
httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
int statusCode=httpClient.executeMethod(postMethod);
// 获得登陆后的 Cookie
Cookie[] cookies = httpClient.getState().getCookies();
StringBuffer tmpcookies = new StringBuffer();
for (Cookie c : cookies) {
tmpcookies.append(c.toString() + ";");
System.out.println("cookies = "+c.toString());
}
if(statusCode==302){//重定向到新的URL
System.out.println("模拟登录成功");
// 进行登陆后的操作
GetMethod getMethod = new GetMethod(dataUrl);
// 每次访问需授权的网址时需带上前面的 cookie 作为通行证
getMethod.setRequestHeader("cookie", tmpcookies.toString());
// 你还可以通过 PostMethod/GetMethod 设置更多的请求后数据
// 例如,referer 从哪里来的,UA 像搜索引擎都会表名自己是谁,无良搜索引擎除外
postMethod.setRequestHeader("Referer", "http://passport.mop.com/");
postMethod.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36");
httpClient.executeMethod(getMethod);
// 打印出返回数据,检验一下是否成功
String text = getMethod.getResponseBodyAsString();
System.out.println(text);
}
else {
System.out.println("登录失败");
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
=============================================
package com.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
public class HttpClientTest {
@Test
public void jUnitTest() {
get();
}
/**
* HttpClient连接SSL
*/
public void ssl() {
CloseableHttpClient httpclient = null;
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream instream = new FileInputStream(new File("d:\\tomcat.keystore"));
try {
// 加载keyStore d:\\tomcat.keystore
trustStore.load(instream, "123456".toCharArray());
} catch (CertificateException e) {
e.printStackTrace();
} finally {
try {
instream.close();
} catch (Exception ignore) {
}
}
// 相信自己的CA和所有自签名的证书
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
// 只允许使用TLSv1协议
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
// 创建http请求(get方式)
HttpGet httpget = new HttpGet("https://localhost:8443/myDemo/Ajax/serivceJ.action");
System.out.println("executing request" + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
System.out.println(EntityUtils.toString(entity));
EntityUtils.consume(entity);
}
} finally {
response.close();
}
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} finally {
if (httpclient != null) {
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* post方式提交表单(模拟用户登录请求)
*/
public void postForm() {
// 创建默认的httpClient实例.
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建httppost
HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
// 创建参数队列
List<namevaluepair> formparams = new ArrayList<namevaluepair>();
formparams.add(new BasicNameValuePair("username", "admin"));
formparams.add(new BasicNameValuePair("password", "123456"));
UrlEncodedFormEntity uefEntity;
try {
uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(uefEntity);
System.out.println("executing request " + httppost.getURI());
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
System.out.println("--------------------------------------");
System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));
System.out.println("--------------------------------------");
}
} finally {
response.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发送 post请求访问本地应用并根据传递参数不同返回不同结果
*/
public void post() {
// 创建默认的httpClient实例.
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建httppost
HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
// 创建参数队列
List<namevaluepair> formparams = new ArrayList<namevaluepair>();
formparams.add(new BasicNameValuePair("type", "house"));
UrlEncodedFormEntity uefEntity;
try {
uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(uefEntity);
System.out.println("executing request " + httppost.getURI());
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
System.out.println("--------------------------------------");
System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));
System.out.println("--------------------------------------");
}
} finally {
response.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发送 get请求
*/
public void get() {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
// 创建httpget.
HttpGet httpget = new HttpGet("http://www.baidu.com/");
System.out.println("executing request " + httpget.getURI());
// 执行get请求.
CloseableHttpResponse response = httpclient.execute(httpget);
try {
// 获取响应实体
HttpEntity entity = response.getEntity();
System.out.println("--------------------------------------");
// 打印响应状态
System.out.println(response.getStatusLine());
if (entity != null) {
// 打印响应内容长度
System.out.println("Response content length: " + entity.getContentLength());
// 打印响应内容
System.out.println("Response content: " + EntityUtils.toString(entity));
}
System.out.println("------------------------------------");
} finally {
response.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 上传文件
*/
public void upload() {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceFile.action");
FileBody bin = new FileBody(new File("F:\\image\\sendpix0.jpg"));
StringBody comment = new StringBody("A binary file of some kind", ContentType.TEXT_PLAIN);
HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("bin", bin).addPart("comment", comment).build();
httppost.setEntity(reqEntity);
System.out.println("executing request " + httppost.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httppost);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
System.out.println("Response content length: " + resEntity.getContentLength());
}
EntityUtils.consume(resEntity);
} finally {
response.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}</namevaluepair></namevaluepair></namevaluepair></namevaluepair>
==================================================
cenzr的博客
目录视图
摘要视图
订阅
异步赠书:9月重磅新书升级,本本经典 SDCC 2017之区块链技术实战线上峰会 程序员9月书讯 每周荐书:ES6、虚拟现实、物联网(评论送书)
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(一)
2016-05-23 22:39 1633人阅读 评论(4) 收藏 举报
版权声明:本文为博主原创文章,未经博主允许不得转载。
最近都在搞和爬虫相关的东西,在搞完学校新闻模拟登录后,就感觉有种一丢丢的成就感,所以心血来潮想自己弄一个教务系统出来。在之前实现模拟登陆的时候本人无法通过HttpWatch进行分析,因为登陆界面是在外网的时候才会出现,所以但是就用手机把网页下载下来进行分析,然后找出需要post的参数,然后实现模拟登录。按照这样的思路,无疑我会用这种思路去实现登录正方系统,但是结果尝试很多次都不行。结果通过HttpWatch观察发现post的时候还有一个隐藏的参数,所以之前才会行不通.
以下是效果图:
实现以上的各种功能其实关键在于把协议给弄清楚。首先需要我们注意的是登录需要帐号,密码和验证码,而通过httpwatch不难发现,其实我们访问整个过程中我们都需要用到我们验证码所带的Cookie。换句话来说,我们要在获取到验证码图片的时候,获取到我们需要的Cookie信息。
以下是HttpWatch抓到验证码的信息:
通过获取验证码,我们会将cookie保存下来,保存下来有什么用?看看下面这图就知道了
以下是我登录正方系统后所抓到的信息:
我们可以看到我们请求登录的时候请求头所带的cookie正是我们刚刚获取到验证码的时候所带的cookie,这就是为什么我们获取到验证码时要保存cookie的原因。
接着我们再来看一下这个post请求里面的信息,里面有__ViewStat(我也不知道是什么,不过一起带着Post就好了)RadioButtonList1(单选按钮:学生...),txtUserName:账号,TextBox2:密码,txtSecretCode:验证码。当然RadioButtonList1后面乱码了我们不知道传什么东西,不过淡定,我们在Stream中可以看到有一个_VIEWSTATE,而这个key所对应的值正是我们所传的所有参数的一个字符串。
__VIEWSTATE=dDwyODE2NTM0OTg7Oz6IMxu8wRHiZevFvdItT8RVFJjAFQ%3D%3D&txtUserName=yournumber&TextBox2=yourkey&txtSecretCode=ctd3&RadioButtonList1=%D1%A7%C9%FA&Button1=&lbLanguage=&hidPdrs=&hidsc=
看吧,根据Stream中的值我们就可以知道RadioButtonList1的值。
说了那么多我们就直接亮出我们的代码把。
获取验证码:
[java] view plain copy
/**
* 验证码功能
*
*/
public static Bitmap getCode()
throws UnsupportedOperationException, Exception {
// 获取验证码
HttpGet secretCodeGet = new HttpGet(secretCodeUrl);
client = new DefaultHttpClient();
HttpResponse responseSecret = client.execute(secretCodeGet);
// 获取返回的Cookie
Cookie = responseSecret.getFirstHeader("Set-Cookie").getValue();
viewState = IOUtils.getViewState(indexUrl, "", "");
InputStream content = responseSecret.getEntity().getContent();
final Bitmap bitmap = BitmapFactory.decodeStream(content);
// client=null;不能执行这个代码因为登录的时候还要用到这个client
return bitmap;
}
当然我们需要获取到_VIEWSTATE:
[java] view plain copy
/**
* 获取隐藏字段的__VIEWSTATE值
*
* @param url
* @param cookie
* @param referer
* @return
* @throws UnsupportedOperationException
* @throws ClientProtocolException
* @throws IOException
*/
public static String getViewState(String url, String cookie, String referer)
throws UnsupportedOperationException, ClientProtocolException,
IOException {
HttpClient client = new DefaultHttpClient();
HttpGet getViewState = new HttpGet(url);
getViewState.setHeader("Cookie", cookie);
getViewState.setHeader("Referer", referer);// 设置头信息
String s = IOUtils.getHtml(client.execute(getViewState).getEntity()
.getContent(), "GB2312");
String viewstate = Jsoup.parse(s).select("input[name=__VIEWSTATE]")
.val();
return viewstate;
}
以上的代码就可以获取到验证码以及对应的Cookie了。
下面我们实现登录的逻辑,按照上面的推理,我们要知道我们要传的参数就是我们下面的参数,记得要带cookie
以下是实现登录功能的代码:
[java] view plain copy
/**
* 登录功能
* @param stuNumber
* @param password
* @param secret
* @return
* @throws ClientProtocolException
* @throws IOException
*/
public static boolean loginUser(String stusNumber, String password,String secret) throws ClientProtocolException, IOException
{
stuNumber=stusNumber;
HttpPost loginPost = new HttpPost(loginUrl);// 创建登录的Post请求
// 阻止自动重定向,目的是获取第一个ResponseHeader的Cookie和Location
loginPost.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
loginPost.setHeader("Cookie", Cookie);// 带上第一次请求的Cookie
List<NameValuePair> nameValuePairLogin = new ArrayList<NameValuePair>();// 封装Post提交参数
nameValuePairLogin
.add(new BasicNameValuePair("__VIEWSTATE", viewState));// 隐藏表单值
nameValuePairLogin
.add(new BasicNameValuePair("txtUserName", stuNumber));// 学号
nameValuePairLogin.add(new BasicNameValuePair("TextBox2", password));// 密码
nameValuePairLogin.add(new BasicNameValuePair("txtSecretCode", secret));// 验证码
nameValuePairLogin.add(new BasicNameValuePair("RadioButtonList1",
identityStu));// 身份,默认学生
nameValuePairLogin.add(new BasicNameValuePair("Button1", ""));
nameValuePairLogin.add(new BasicNameValuePair("lbLanguage", ""));
nameValuePairLogin.add(new BasicNameValuePair("hidPdrs", ""));
nameValuePairLogin.add(new BasicNameValuePair("hidsc", ""));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(
nameValuePairLogin, "GB2312");
loginPost.setEntity(entity);
HttpResponse responseLogin = client.execute(loginPost);
// 第三步:判断提交数据是否成功,成功返回302
if (responseLogin.getStatusLine().getStatusCode() == 302) {
// 如果提交成功,带着Cookie请求重定向的main页面,并获取学生姓名
HttpGet mainGet = new HttpGet(mainUrl + stuNumber);
mainGet.setHeader("Cookie", Cookie);
mainGet.setHeader("Referer", loginUrl);
HttpResponse responseMain = client.execute(mainGet);
InputStream is = responseMain.getEntity().getContent();
String html = "";
try {
html = IOUtils.getHtml(is, "GB2312");
// System.out.println(html);
} catch (Exception e) {
System.out.println("解析html失败!");
e.printStackTrace();
}
stuName = Jsoup.parse(html).getElementById("xhxm").text();
System.out.println("登录成功!欢迎您:" + stuName);
client=null;
return true;
} else {
System.out.println("登录失败!");
return false;
}
}
以上两部分代码就完美的实现了登录功能。
虽然看起来很简单,但是实际上这可能是这个app中最重要的一个部分。这个分析不仅仅用于这个app也可以用于其他网站的登录分析,比如开源中国,CSDN等,如果有兴趣都可以自己弄一个属于自己的一个客户端出来。
今天就先介绍登录的部分,下次再介绍一下如何获取课表,成绩,以及空课室查询
顶
2
踩
0
上一篇handler和looper在子线程的调用
下一篇Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(二)
相关文章推荐
? 教务管理系统-学生查询个人课表
? 携程机票大数据基础平台架构演进-- 许鹏
? 数据抓包(网络爬虫)-正方教务管理系统登录
? Python可以这样学--董付国
? 数据抓包(网络爬虫)-正方教务管理系统登录后获取自己的课程表
? 一步一步学Spring Boot
? Android客户端利用OKhttp3,Jsoup简洁详细实现登录教务官网,并获取成绩,课程表,培养计划等
? 深入浅出C++程序设计
? python爬虫登录正方教务管理系统获取成绩数据
? Android Material Design 新控件
? Android实现模拟登陆正方系统查成绩
? 机器学习需要用到的数学知识
? Python项目模拟登录学校正方教务系统抓取课程表。
? 江西理工大学正方教务系统查成绩(模拟登录)
? 用java模拟登录正方教务系统,抓取课表和个人成绩等数据
? 正方系统成绩查询详细技术贴
查看评论
4楼
默默n
2016-11-25 20:17发表 [回复]
我用httpwatch抓取的包中没有cookie
3楼
默默n
2016-11-25 19:10发表 [回复]
935204768@qq.com
2楼
默默n
2016-11-25 19:09发表 [回复]
我试了显示请求被拒绝,求源码
1楼
jieerAPP
2016-11-20 12:13发表 [回复]
博主,可以发源码给我吗?376069963@qq.com
发表评论
用 户 名:
MAGANG255
评论内容:
插入代码
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
个人资料
[访问我的空间]
cenzr
1
访问:13687次
积分:332
等级:
排名:千里之外
原创:17篇
转载:12篇
译文:0篇
评论:8条
文章搜索
文章分类
java爬虫(1)
文章存档
2017年06月(8)
2017年05月(5)
2016年10月(2)
2016年05月(2)
2016年04月(10)
展开
阅读排行
Kotlin 资源大全 - 学 Kotlin 看这一篇教程就够了(3476)
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(一)(1632)
关于解决多个viewpager嵌套所遇到的事件传递问题(1313)
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(二)(1149)
Android线程池(788)
JAVA面试题整理(598)
Kotlin在Android上令人惊叹的技巧(585)
Android面试题整理(290)
handler和looper在子线程的调用(268)
那些年收藏的Android开源库集合(控件)(266)
评论排行
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(一)(4)
第一篇博客(1)
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(二)(1)
Android开发中那些让你相见恨晚的方法、类或接口(2)(1)
怎么在Activity中获取指定控件的宽高(0)
Android线程池(0)
Android IntentService完全解析 当Service遇到Handler(0)
Android HandlerThread 完全解析(0)
android的消息机制(0)
学习笔记之Service(0)
推荐文章
* CSDN日报20170828——《4个方法快速打造你的阅读清单》
* Android检查更新下载安装
* 动手打造史上最简单的 Recycleview 侧滑菜单
* TCP网络通讯如何解决分包粘包问题
* SDCC 2017之区块链技术实战线上峰会
* 快速集成一个视频直播功能
最新评论
哎,个人开发者,想靠 app 赚钱真的很困难啊!
cenzr: @WilliamXiaoLiang:qq 551647899
Android开发中那些让你相见恨晚的方法、类或接口(2)
费费德勒: 赞
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(二)
1ess: 真正的Jsoup解析竟然只用了一个.size() 方法就省略了,看完我泪奔。。
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(一)
默默n: 我用httpwatch抓取的包中没有cookie
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(一)
默默n: 935204768@qq.com
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(一)
默默n: 我试了显示请求被拒绝,求源码
Android实现正方系统的登录以及课程表,成绩获取和空课室的查询(一)
jieerAPP: 博主,可以发源码给我吗?376069963@qq.com
第一篇博客
cenzr: 加油加油
公司简介
|
招贤纳士
|
广告服务
|
联系方式
|
版权声明
|
法律顾问
|
问题报告
|
合作伙伴
|
论坛反馈
网站客服
杂志客服
微博客服
webmaster@csdn.net
400-660-0108|北京创新乐知信息技术有限公司 版权所有|江苏知之为计算机有限公司|江苏乐知网络技术有限公司
京 ICP 证 09002463 号|Copyright ? 1999-2017, CSDN.NET, All Rights Reserved
GongshangLogo
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。