一、HttpClient vs HttpUrlConnection
抓取一张网页的内容,通常使用HttpClient 、HttpUrlConnection,首先查了下这两个类的区别:
java.net 包中提供了HttpURLConnection来访问 HTTP 协议,这个是java的标准类,什么都没封装,用起来太原始,不方便
Apache的HttpClient模块,用来提供高效的、最新的、功能丰富的支持 HTTP 协议工具包,是一个增强版的HttpURLConnection,HttpURLConnection可以做的事情HttpClient全部可以做;HttpURLConnection没有提供的有些功能,HttpClient也提供了,但它只是关注于如何发送请求、接收响应,以及管理HTTP连接。 由于做了很多封装,性能上要比HttpURLConnection差一些,但用着方便,这里就基于此类来实现爬虫。
二、获取一张静态网页的内容
(1)org.apache.http.impl.client.CloseableHttpClient
它是一个抽象类,它实现了org.apahce.http.client.HttpClient接口(共有方法:多个execute()、getConnectionManager()、getParams())的execute()方法。我们知道抽象类是不能用new关键字建立实例的,只能被当作父类被其它子类继承。然后根据类的多态性,将拥有抽象类类型的引用变量指向它的子类对象,这样就可以使用抽象类中的普通方法以及在其子类中已重写的抽象方法。当然,还可以通过过渡工厂类可以更灵活的获取抽象类的实例
(2)使用HttpClient发送请求、接收响应的步骤
1. 创建HttpClient对象。
2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参 数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用 HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
6. 释放连接。无论执行方法是否成功,都必须释放连接。(关于释放连接,应该不是必须滴,我没处理也抓的好好地。。。)
三、 解析DOM文档
要获取一张网页中我们所需要的内容,就必须解析文档,jsoup就是一款公认的、迄今最强大的解析html的工具
http://www.open-open.com/jsoup/
来看关键代码:
public HttpGet getNormalHttpGet(){
HttpGet httpGet = new HttpGet(this.url);
httpGet.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;");
httpGet.addHeader("Accept-Language", "zh-cn");
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3");
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(SOCKETTIMEOUT).setConnectTimeout(CONNECTTIMEOUT).build();
httpGet.setConfig(requestConfig);
return httpGet;
}
private String convertToThisCharset(String rst){
try{
String tmp = new String( rst.getBytes(this.encode), "utf-8");
return tmp;
}catch (Exception e){
}
return null;
}
public String run(){
HttpResponse response = null;
httpGet=getNormalHttpGet();
try {
response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
String respContent = EntityUtils.toString(entity, this.encode).trim();
if( !this.encode.equals("UTF-8") )
respContent = convertToThisCharset( respContent );
return respContent;
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
}
return null;
}
public void get(String url)//赋值给this.doc
{
this.pageContent = run();
if (this.pageContent != null){
this.doc = Jsoup.parse(this.pageContent);
}
else{
System.out.println( " ... crawled failed.");
}
}
public static void main(String[] args){
Crawler c=new Crawler(url);
c.get(url);
System.out.println(c.getDoc());
Elements price = c.getDoc().select("div.tm-promo-price span.tm-price");
if ((price == null) || (price.size() == 0)) {
System.out.println("null");
}else{
System.out.println(price.first().text());
}
}
运行结果就是这样的:
这是页面的开始部分,也就是整个网页的抓取,内容就不做完全展示了。
但是会发现天猫价格打印下来为空,这是为什么呢?放心,根据css选择器获取dom元素的代码是没问题哒,之所以获取不到,是因为这个价格是ajax动态加载的,普通的抓取静态网页爬虫抓不下来,此处留一坑 ,下期来补,敬请期待下期——phantomjs抓取ajax动态加载网页。