1. java 如何模拟浏览器调用rest api接口
packagecom.demo;
importjava.io.BufferedReader;
importjava.io.IOException;
importjava.io.InputStreamReader;
importjavax.xml.bind.DatatypeConverter;
importorg.apache.http.HttpResponse;
importorg.apache.http.client.methods.HttpGet;
importorg.apache.http.impl.client.DefaultHttpClient;
publicclassrestTest{
publicstaticvoidmain(String[]args){
try{
DefaultHttpClientClient=newDefaultHttpClient();
HttpGethttpGet=newHttpGet("你的地址");
Stringencoding=DatatypeConverter.printBase64Binary("admin:admin".getBytes("UTF-8"));
httpGet.setHeader("Authorization","Basic"+encoding);
HttpResponseresponse=Client.execute(httpGet);
System.out.println("response="+response);
BufferedReaderbreader=newBufferedReader(newInputStreamReader(response.getEntity().getContent()));
StringBuilderresponseString=newStringBuilder();
Stringline="";
while((line=breader.readLine())!=null){
responseString.append(line);
}
breader.close();
StringrepsonseStr=responseString.toString();
System.out.println("repsonseStr="+repsonseStr);
}catch(IOExceptione){
e.printStackTrace();
}
}
}
2. 使用java开源工具httpclient怎么使用
使用java开源工具httpClient及jsoup抓取解析网页数据
来源:iteye,原文
今天做项目的时候遇到这样一个需求,需要在网页上展示今日黄历信息,数据格式如下
公历时间:2016年04月11日星期一
农历时间:猴年三月初五
天干地支:丙申年壬辰月癸亥日
宜:求子祈福开光祭祀安床
忌:玉堂(黄道)危日,忌出行
主要包括公历/农历日期,以及忌宜信息的等。但是手里并没有现成的数据可供使用,怎么办呢?革命前辈曾经说过,没有枪,没有炮,敌(wang)人(luo)给我们造!网络上有很多现成的在线万年历应用可供使用,虽然没有现成接口,但是我们可以伸出手来,自己去拿。也就是所谓的数据抓取。
这里介绍两个使用的工具,httpClient以及jsoup,简介如下:
HttpClient是ApacheJakartaCommon下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如ApacheJakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。
httpClient使用方法如下:
1.创建HttpClient对象。
2.创建请求方法的实例,并指定请求URL。
3.调用HttpClient对象的execute(HttpUriRequestrequest)发送请求,该方法返回一个HttpResponse。
4.调用HttpResponse相关方法获取相应内容。
5.释放连接。
jsoup是一款Java的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
需要更多信息可以参见官网下载地址
httpClient:http://hc.apache.org/httpcomponents-client-5.0.x/index.html
jsoup:http://jsoup.org/
接下来我们直接上代码,这里我们抓取2345在线万年历的数据http://tools.2345.com/rili.htm
首先我们定义一个实体类Almanac来存储黄历数据
Almanac.java1packagecom.likx.picker.util.bean;2
3/**4
*万年历工具实体类5
*
6
*@author溯源blog7
*2016年4月11日8
*/9publicclassAlmanac{10
privateStringsolar;
/*阳历e.g.2016年4月11日星期一*/11
privateStringlunar;
/*阴历e.g.猴年三月初五*/12
privateStringchineseAra;
/*天干地支纪年法e.g.丙申年壬辰月癸亥日*/13
privateStringshould;
/*宜e.g.求子祈福开光祭祀安床*/14
privateStringavoid;
/*忌e.g.玉堂(黄道)危日,忌出行*/1516
publicStringgetSolar(){17
returnsolar;18
}1920
publicvoidsetSolar(Stringdate){21
this.solar=date;22
}2324
publicStringgetLunar(){25
returnlunar;26
}2728
publicvoidsetLunar(Stringlunar){29
this.lunar=lunar;30
}3132
publicStringgetChineseAra(){33
returnchineseAra;34
}3536
publicvoidsetChineseAra(StringchineseAra){37
this.chineseAra=chineseAra;38
}3940
publicStringgetAvoid(){41
returnavoid;42
}4344
publicvoidsetAvoid(Stringavoid){45
this.avoid=avoid;46
}4748
publicStringgetShould(){49
returnshould;50
}5152
publicvoidsetShould(Stringshould){53
this.should=should;54
}5556
publicAlmanac(Stringsolar,Stringlunar,StringchineseAra,Stringshould,57
Stringavoid){58
this.solar=solar;59
this.lunar=lunar;60
this.chineseAra=chineseAra;61
this.should=should;62
this.avoid=avoid;63
}64}
然后是抓取解析的主程序,写程序之前需要在官网下载需要的jar包
AlmanacUtil.javapackagecom.likx.picker.util;importjava.io.IOException;importjava.text.SimpleDateFormat;importjava.util.Calendar;importjava.util.Date;importorg.apache.http.HttpEntity;importorg.apache.http.ParseException;importorg.apache.http.client.ClientProtocolException;importorg.apache.http.client.methods.CloseableHttpResponse;importorg.apache.http.client.methods.HttpGet;importorg.apache.http.impl.client.CloseableHttpClient;importorg.apache.http.impl.client.HttpClients;importorg.apache.http.util.EntityUtils;importorg.jsoup.Jsoup;importorg.jsoup.nodes.Document;importorg.jsoup.nodes.Element;importorg.jsoup.select.Elements;/***<STRONG>类描述</STRONG>:
2345万年历信息爬取工具<p>*
*@version1.0<p>*@author溯源blog*
*<STRONG>创建时间</STRONG>:2016年4月11日下午14:15:44<p>*<STRONG>修改历史</STRONG>:<p>*<pre>*修改人
修改时间
修改内容*---------------
-------------------
-----------------------------------*</pre>*/publicclassAlmanacUtil{
/**
*单例工具类
*/
privateAlmanacUtil(){
}
/**
*获取万年历信息
*@return
*/
publicstaticAlmanacgetAlmanac(){
Stringurl="http://tools.2345.com/rili.htm";
Stringhtml=pickData(url);
Almanacalmanac=analyzeHTMLByString(html);
returnalmanac;
}
/*
*爬取网页信息
*/
privatestaticStringpickData(Stringurl){
CloseableHttpClienthttpclient=HttpClients.createDefault();
try{
HttpGethttpget=newHttpGet(url);
CloseableHttpResponseresponse=httpclient.execute(httpget);
try{
//获取响应实体
HttpEntityentity=response.getEntity();
//打印响应状态
if(entity!=null){
returnEntityUtils.toString(entity);
}
}finally{
response.close();
}
}catch(ClientProtocolExceptione){
e.printStackTrace();
}catch(ParseExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}finally{
//关闭连接,释放资源
try{
httpclient.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
returnnull;
}
/*
*使用jsoup解析网页信息
*/
(Stringhtml){
StringsolarDate,lunarDate,chineseAra,should,avoid="";
Documentdocument=Jsoup.parse(html);
//公历时间
solarDate=getSolarDate();
//农历时间
ElementeLunarDate=document.getElementById("info_nong");
lunarDate=eLunarDate.child(0).html().substring(1,3)+eLunarDate.html().substring(11);
//天干地支纪年法
ElementeChineseAra=document.getElementById("info_chang");
chineseAra=eChineseAra.text().toString();
//宜
should=getSuggestion(document,"yi");
//忌
avoid=getSuggestion(document,"ji");
Almanacalmanac=newAlmanac(solarDate,lunarDate,chineseAra,should,avoid);
returnalmanac;
}
/*
*获取忌/宜
*/
(Documentdoc,Stringid){
Elementelement=doc.getElementById(id);
Elementselements=element.getElementsByTag("a");
StringBuffersb=newStringBuffer();
for(Elemente:elements){
sb.append(e.text()+"");
}
returnsb.toString();
}
/*
*获取公历时间,用yyyy年MM月dd日EEEE格式表示。
*@returnyyyy年MM月dd日EEEE
*/
(){
Calendarcalendar=Calendar.getInstance();
DatesolarDate=calendar.getTime();
SimpleDateFormatformatter=newSimpleDateFormat("yyyy年MM月dd日EEEE");
returnformatter.format(solarDate);
}}
为了简单明了我把抓取解析抽象成了几个独立的方法,
其中pickData()方法使用httpClient来抓取数据到一个字符串中(就是在网页上点击查看源代码看到的HTML源码),analyzeHTMLByString()方法来解析抓取到的字符串,getSuggestion方法把抓取方法类似的宜忌数据抽象到了一起,另外因为公历时间可以很容易的自己生成就没有在网页上爬取。
然后下面是一个测试类简单测试下效果:AlmanacUtilTest.javapackagecom.likx.picker.util.test;publicclassAlmanacUtilTest{
publicstaticvoidmain(Stringargs[]){
Almanacalmanac=AlmanacUtil.getAlmanac();
System.out.println("公历时间:"+almanac.getSolar());
System.out.println("农历时间:"+almanac.getLunar());
System.out.println("天干地支:"+almanac.getChineseAra());
System.out.println("宜:"+almanac.getShould());
System.out.println("忌:"+almanac.getAvoid());
}}
运行结果如下:
集成到实际项目中效果是这样的:
另外最近博客一直没怎么更新,因为最近考虑到技术氛围的原因,离开了对日外包行业,前往一家互联网公司就职。说一下最近的感受,那就是一个程序员最核心的竞争力不是学会了多少框架,掌握多少种工具(当然这些对于程序员也不可或缺),而是扎实的基础以及快速学习的能力,比如今天这个项目,从对httpClient,jsoup工具一无所知到编写出Demo代码总计大概1个多小时,在之前对于我来说是不可想象的,在技术氛围浓厚的地方快速get技能的感觉,非常好。
当然本例只是一个非常浅显的小例子,网页上内容也很容易抓取,httpClient及jsoup工具更多强大的地方没有体现到,比如httpClient不仅可以发送get请求,而且可以发送post请求,提交表单,传送文件,还比如jsoup最强大的地方在于它支持仿jquery的选择器。本例仅仅使用了最简单的document.getElementById()匹配元素,实际上jsoup的选择器异常强大,可以说它就是java版的jquery,比如这样:Elementslinks=doc.select("a[href]");//awithhrefElementspngs=doc.select("img[src$=.png]");
//imgwithsrcending.pngElementmasthead=doc.select("div.masthead").first();
//divwithclass=mastheadElementsresultLinks=doc.select("h3.r>a");//directaafterh3
3. 关于JAVA模拟发送post请求并响应内容
如果你是用java的api实现的模拟post请求,那么你需要在你之前构造的http request的header里加上
Cookie:名字=值 然后统一专包装成你的conenction的OutputStream。
建议你用属apache的HttpClient api项目,里面有专门处理cookie的api,这样事情就简单许多。
4. 在Java中怎么获得网页状态码,比如:404等
StatusCode 似乎 Servelt API中,无论是 Filter Wrapper Reponse,都没有提供 get的方法。
以下是一个从网上找到的解决方案。
1.先实现一个对Response的包装器:
public class StatusExposingServletResponse extends HttpServletResponseWrapper {
private int httpStatus;
public StatusExposingServletResponse(HttpServletResponse response) {
super(response);
}
@Override
public void sendError(int sc) throws IOException {
httpStatus = sc;
super.sendError(sc);
}
@Override
public void sendError(int sc, String msg) throws IOException {
httpStatus = sc;
super.sendError(sc, msg);
}
@Override
public void setStatus(int sc) {
httpStatus = sc;
super.setStatus(sc);
}
public int getStatus() {
return httpStatus;
}
}
2, 然后实现一个Filter来替换原始的HttpServletResponse,这样你就可以在Filter里面取到statusCode了
public class StatusReportingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
StatusExposingServletResponse response = new StatusExposingServletResponse((HttpServletResponse)res);
chain.doFilter(req, response);
int status = response.getStatus();
// report 在这儿你就得到状态码了。
}
public void init(FilterConfig config) throws ServletException {
//empty
}
public void destroy() {
// empty
}
}
5. 急急急!!!简单问题,java的response和request用法,需要import那个包
servlet-api.jar这个包.在tomcat等服务器中可找到此包.
request对象的主要方法有:
1. getAttribute( String name )
返回name指定的属性值,如果指定的属性值不存在,则会返回null值.
2. getAttributeNames()
返回request对象的所有属性的名字,其结果是一个类举(Enumeration)类的实例.
3. getCookies()
返回客户端的Cookie对象,结果是一个Cookies数组.
4. getHeader( String name )
返回指定名字的request Header的所有值,其结果也是一个类举类的实例.
5. getHeaderNames()
返回所有request Header 的名字,其结果也是一个类举类的实例.
6. getMethod()
获得客户端向服务器端传送数据的方法,如get,post等.
7. getParameter( String name )
获得客户端传送给服务器的参数值,该参数是由name指定的.
8. getParameterNames()
返回客户端传给服务器摘的所有参数的名字,其结果也是一个类举类的实例.
9. getParameterValues( String name )
获得指定参数的所有值,参数由name指定.
10. getProtocol()
获得客户端向服务器端传送数据所依据的协议的名称.
11. getQueryString()
获得查询字符串,该字符串由客户端以get方法向服务器端传送.
12. getRequestURI()
获取发出请求字符串的客户端的地址.
13. getRemoteAddr()
获取客户端的IP地址.
14. getRemoteHost()
获取客户端的名字.
15. getServerName()
获取服务器的名字.
16. getServerPort()
获取服务器的名字..
17. setAttribute( String name , java.lang.Object objt )
设置名字为name的request参数的值,该值是由java.lang.Object类型的objt指定
response对象的主要方法有:
1. addCookie( Cookie cook )
添加一个Cookie对象,用来保存客户端用户信息.
2. addHeader( String name , String value )
添加HTTP文件头信息,该Header将传到客户端去,如果已经同的Header存在,则覆盖已有的Header.
3. containsHeader( String name )
判断指定名字的HTTP文件头是否已经存在,然后返回真假布尔值.
4. sendError( int )
向客户端发送错误的信息.
5. setHeader( String name , String value )
设置指定的HTTP文件的值,如果该值已经存在,则新值会覆盖原有的旧值.
6. retrofit2.0+RxJava结合使用时,怎么才能通过post请求返回一个response对象
1.首先定义带泛型的返回结果,RetrofitAPI的原生结果映射为这种形式:classResult{StringResultMessage;intResultCode;TData;}2.处理错误的方法和@朱诗雄前辈方法差不多,放到作为静态方法放到RetroUtil里,这里ApiException为自己定义的一个异常,放入错误码和错误信息(错误码不止一个):staticObservableflatResult(Resultresult){returnObservable.create(subscriber->{switch(result.ResultCode){caseConstants.SUCCESS_CODE:subscriber.onNext(result.Data);break;caseConstants.ERROR_CODE:subscriber.onError(newApiException(result.ResultCode,result.ResultMessage);break;default://}subscriber.onCompleted();}});}3.在API包装类对于上述Result格式的返回结果,统一调用flatMap(RetroUtil::flatResult)后的API。这样每个API的返回结果就是Observable的形式并且在errorHandler中统一处理错误了。//接口方法Observable>getUserInfo();//包装后的方法ObservablegetUserInfo(){returnmApi.getUserInfo.flatMap(RetroUtil::flatResult);}//调用时apiWrapper.getUserInfo().subscrible(user->{//处理正常逻辑},error->{//处理错误逻辑和异常,这里封装时通常也会统一处理,//提供一个默认的Action1参数,弹出//throwable的message打印日志等});