HTMLParser HttpClient Firebug结合解析网页

题外话

已经很长时间没有写作了,原因颇多,世事变迁之快,迅雷不及掩耳,技术之路上仍需努力,闲话少说,开整。

背景

一直以来ITS Team都痛苦与build的下载工作,具体流程如下图所示。

1. 登录BuildVault网站。
































2. 点击distribution进入二级页面。





































3. 选择相应的产品下载。











































4. 获取一个下载build的URL,要么下载到本地这么还得再传到server上deploy,SCP或者FTP都是件极其痛苦的事情,或者利用CURL命令在server上直接下载,为什么不二次开发这个站点呢?远程在Server上执行curl脚本之间下载呢? 那么好,设想是好的,我们需要考虑很多B/S架构应用程序,UI是一大块,后台的话必须要有API能解析BuildVault网站,所以我们就必须自己分析BuildVault网站的内容,截取出我们自己想要的内容。此时进入正题。利用Firebug+httpclient+htmlparser解析网页内容。

说明

1) HTMLParser

htmlparser是一个纯的java写的html解析的库,它不依赖于其它的java库文件,主要用于改造或提取html。它能超高速解析html,而且不会出错。毫不夸张地说,htmlparser就是目前最好的html解析和分析的工具。无论你是想抓取网页数据还是改造html的内容,用了htmlparser绝对会忍不住称赞。

2) HttpClient

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

以下列出的是 HttpClient 提供的主要的功能,
实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
支持自动转向
支持 HTTPS 协议
支持代理服务器等

3) Firebug

Firebug是Firefox下的一款开发类插件,现属于Firefox的五星级强力推荐插件之一。它集HTML查看和编辑、Javascript控制台、网络状况监视器于一体,是开发JavaScript、CSS、HTML和Ajax的得力助手。Firebug已经和firefox无缝的紧密联合了,打开它可以看到它有以下几个Tab按钮,这里我们主要用到“Net”,用它来监视HTTP请求。

Console HTML CSS Script Dom Net
控制台 Html查看器 Css查看器 脚本条时期 Dom查看器 网络状况监视
























































4) 截获请求参数

如果你从事过web开发的话,对于HTTP POST/GET提交请求一定不会陌生,如下HTML片段,这里有一些input tag还有hidden的tag,这些都会在用户点击submit的时候一并提交给服务器,服务器可以取到这些input的value。

<form method=”post” action=”http://justice.svl.ibm.com/cgi-bin/build-vault.cgi”>

<table border=”0″>

<tbody>

<tr>

<td align=”right”>Intranet ID</td>

<td><input type=”text” size=”30″ value=”” name=”id”/></td>

</tr>

<tr><td align=”right”>Password</td>

<td><input type=”password” value=”” name=”password”/>

</td>

</tr>

<tr>

<td align=”right” colspan=”2″>

<span class=”button-blue”><input type=”submit” value=”Sign In” name=””/></span>

</td>

</tr>

</tbody>

</table>

<input type=”hidden” value=”” name=”path”/>

<input type=”hidden” value=”Browse” name=”action”/>

<input type=”hidden” value=”” name=”display”/>

<input type=”hidden” value=”Normal” name=”mode”/>

</form>

那么我们的firebug集成在浏览器中当然可以截获这些随从POST/GET请求发送给服务器的这些参数了,打开“网络”->”Post”即可看到。

























5) 截获HTTP请求头与响应头(request/response header)

打开“网络”->“Headers”









































6) 查看响应HTML代码

请求提交后,服务器响应完毕会传送回浏览器HTML代码进行渲染才看到展示给我们的网页。

打开”网络”->”响应”





































代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
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.PostMethod;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.HasChildFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.tags.InputTag;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
 
 
public class Test {
 
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		HttpClient client = new HttpClient();
		client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); 
 
		/**
		 * 步骤1. 登陆
		 */
		PostMethod post = new PostMethod("http://justice.svl.ibm.com/cgi-bin/build-vault.cgi");
 
		// 设置请求头信息- 方法1
		post.setRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
		post.setRequestHeader("Connection" , "Keep-Alive");
		post.setRequestHeader("Keep-Alive", "300");
		post.setRequestHeader("Host", "justice.svl.ibm.com");
 
		// 设置请求头信息- 方法2
		//	List<Header> headers = new ArrayList<Header>();  
		//	headers.add(new Header("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)"));  
		//	headers.add(new Header("Connection", "Keep-Alive"));  
		//	headers.add(new Header("Keep-Alive", "300"));  
		//	headers.add(new Header("Host", "justice.svl.ibm.com"));  
		//	client.getHostConfiguration().getParams().setParameter("http.default-headers", headers); 
 
		// 设置请求参数,这些参数不同于HTTP头中的参数,它们是放在FORM里提交给服务器的参数
		NameValuePair username = new NameValuePair("id","xxxxxx@123.com");
		NameValuePair password = new NameValuePair("password", "xxxxxxxx");
		NameValuePair action = new NameValuePair("action", "Browse");
		NameValuePair mode = new NameValuePair("mode", "Normal");
		post.setRequestBody(new NameValuePair[] { username, password, action, mode});
 
		// 开始执行,发送请求到服务器
		client.executeMethod(post);
 
		// 打印HTTP返回码
		System.out.println("~~~~~~~~~~~~~ Print status code begin ~~~~~~~~~~~~~~~~~~");
        int status = post.getStatusCode();
        System.out.println(status);
		System.out.println("~~~~~~~~~~~~~ Print status code end ~~~~~~~~~~~~~~~~~~");
 
		// 打印Cookie信息,如果有的话
		System.out.println("~~~~~~~~~~~~~ Print Cookie begin ~~~~~~~~~~~~~~~~~~");	
        Cookie[] cookies=client.getState().getCookies();
        String tmpcookies=""; 
        for(Cookie c:cookies){ 
            tmpcookies=tmpcookies+c.toString()+";"; 
            System.out.println(c); 
        } 
        System.out.println(tmpcookies); 
		System.out.println("~~~~~~~~~~~~~ Print Cookie end ~~~~~~~~~~~~~~~~~~");
 
		// 打印请求头消息
        Header[] req = post.getRequestHeaders();
		System.out.println("~~~~~~~~~~~~~ Print request header begin ~~~~~~~~~~~~~~~~~~");	
        for(int i = 0;i <req.length; i ++) 
        { 
          System.out.println(req[i].getName()+"||"+req[i].getValue()); 
        } 
		System.out.println("~~~~~~~~~~~~~ Print request header end ~~~~~~~~~~~~~~~~~~");	
 
		// 打印响应头消息
        Header[] heard = post.getResponseHeaders(); 
		System.out.println("~~~~~~~~~~~~~ Print response header begin ~~~~~~~~~~~~~~~~~~");	
                    for(int i = 0;i <heard.length; i ++) 
                    { 
                      System.out.println(heard[i].getName()+"||"+heard[i].getValue()); 
                    } 
        System.out.println("~~~~~~~~~~~~~ Print response header end ~~~~~~~~~~~~~~~~~~");	
 
		// 打印响应内容Body
		InputStream is = post.getResponseBodyAsStream();
		String returnStr = convertStreamToString(is);
		System.out.println(returnStr);
 
		// 断开连接
        post.releaseConnection();
 
        // 利用Htmlparser解析返回的body,获取网页中的session值
        // 这里这个site是把session放在HTML返回给客户端的,所以用这种方法,如果放在cookie里就从cookie里取
        Parser parser  =  new Parser(returnStr);
        NodeFilter filter=new AndFilter(new TagNameFilter("input"),new HasAttributeFilter("type","hidden"));
        NodeList list = parser.extractAllNodesThatMatch(filter); 
        Node[] nodes = list.toNodeArray();
        InputTag textnode = (InputTag) (nodes[0]);
        String line = textnode.getAttribute("value");
        System.out.println("session=" + line);
 
		/**
		 * 步骤2. 获取我们想要的的字符串(产品列表)
		 */
        PostMethod post2 = new PostMethod("http://justice.svl.ibm.com/cgi-bin/build-vault.cgi?path=/distributions&session=" + line);
 
        // 设置请求参数,这些参数不同于HTTP头中的参数,它们是放在FORM里提交给服务器的参数
        NameValuePair path = new NameValuePair("path", "/distributions");
		NameValuePair session = new NameValuePair("session", line);
		post2.setRequestBody(new NameValuePair[] { path, session});
		// 开始执行,发送请求到服务器
        client.executeMethod(post2);
 
        // 打印响应内容Body
		InputStream is2 = post2.getResponseBodyAsStream();
		String returnStr2 = convertStreamToString(is2);
		System.out.println(returnStr2);
 
		// 利用Htmlparser解析返回的body,或许我们想得到的字符串
        // 我们想得到的列表的节点HTML为<td><a href="http://justice.svl.ibm.com/cgi-bin/build-vault.cgi?path=distributions/Cognos-BI-8.4-Rebranding&session=5cX6c38KUsGhU4O7NBhElobakgNro8PI">Cognos-BI-8.4-Rebranding</a></td>
        Parser parser2  =  new Parser(returnStr2);
        NodeFilter filter2=new AndFilter(new TagNameFilter("TD"),new HasChildFilter(new TagNameFilter ("A")));
        NodeList list2 = parser2.extractAllNodesThatMatch(filter2); 
        Node[] nodes2 = list2.toNodeArray();
        for (int i = 0; i < nodes2.length; i++) {
         Node node = nodes2[i].getFirstChild();
         if (node instanceof LinkTag) {
        	    LinkTag link = (LinkTag) node;
        	    line = link.getLink();
          String tmp = link.getLinkText();
          System.out.println(tmp); //打印我们的产品列表
         }
        }
 
	}
 
	public static String convertStreamToString(InputStream is)
			throws UnsupportedEncodingException {
 
		BufferedReader reader = new BufferedReader(new InputStreamReader(is,
				"UTF-8"));
		StringBuilder sb = new StringBuilder();
 
		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				reader.close();
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
 
		return sb.toString();
	}
 
}
P.S. 步骤二的参数从何而来? 看下图,firebug告诉我们这个请求的参数:





























~~~~~~~~~~~~~~~~~~~~~~~~~ THE END ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Leave a Comment.