读取过程分析

本文只是简单的读取网页源代码,用到的Java类很有限。

本文针对的目标网页都是一些简单的网页,这些网页不能加密,不能压缩(将网页代码去除空格、换行等,压缩成一行),不存在需要经过一次或n次跳转才能到达。PS:至少目前90%以上的网站都是这种简单的网站。

首先介绍一下HTTP请求头包含的内容:

  1. Accept:客户端希望接收的MIME数据类型,所有的数据类型可以参考http://tool.oschina.net/commons/
  2. Accept-Encoding:浏览器支持的编码类型,包括gzip,defult,compress,identity。
  3. Accept-Language:浏览器支持的语言类型。zh-CN代表汉语,en代表英文。
  4. User-Agent:浏览器的标识,方便服务器识别。

Java代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;

import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.nodes.TagNode;
import org.htmlparser.util.ParserException;
import org.htmlparser.util.SimpleNodeIterator;

public class WebPageSourceCode {

    private String pageEnCode;

    public static void main(String[] args) {
        String path = "http://www.hao123.com/";
        String encode = "UTF-8";
        WebPageSourceCode wb = new WebPageSourceCode();
        String sourceCode = wb.getPageSourceCode(path, encode);
        //http返回的头信息中没有包含网页的编码格式
        if(wb.getPageEnCode() == null){
            encode = getEncodeFromMeta(sourceCode);
            sourceCode = wb.getPageSourceCode(path, encode);
        }
        System.out.println(sourceCode);
    }

    /**
     *这里涉及到一些htmlparser包的一些知识,以后再说。
     *下面使用UTF-8格式解析网页源码,可能存在中文乱码,但是我们要得到meta标签的内容
     *而meta标签的内容是英文,所以中文乱码无所谓
     */
    private static String getEncodeFromMeta(String sourceCode) {
        Parser p = Parser.createParser(sourceCode, "UTF-8");
        String encode = "";
        try {
            SimpleNodeIterator sni = p.extractAllNodesThatMatch(new TagNameFilter("meta")).elements();
            while(sni.hasMoreNodes()){
                Node node = sni.nextNode();
                TagNode tag = new TagNode();
                tag.setText(node.toHtml());
                String temp = null;
                if((temp = tag.getAttribute("content")) != null &&
                        temp.contains("text/html") && temp.contains("charset")){
                    encode = temp.substring(temp.indexOf("=")+1);
                }
            }
        } catch (ParserException e) {
            System.out.println("HtmlParser库无法使用!");
            e.printStackTrace();
        }
        return encode.trim();
    }

    public String getPageSourceCode(String path,String encode){
        StringBuffer sb = new StringBuffer();
        try {
            //1.构建URL对象
            URL url = new URL(path);
            //2.打开URL链接
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //3.设置请求头属性
            conn.setConnectTimeout(5000);//设置响应时间为5s
            conn.setReadTimeout(3000);
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "*/*");
            conn.setRequestProperty("Accept-Encoding", "gzip,default");
            conn.setRequestProperty("User-Agent", 
                    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) "
                    + "Chrome/23.0.1271.64 Safari/537.11");
            //4.得到响应码
            int responseCode = conn.getResponseCode();
            Map<String, List<String>> responseHead = null;
            if(responseCode == HttpURLConnection.HTTP_OK){
                responseHead = conn.getHeaderFields();
                //printHead(responseHead);
                //5.得到网页内容的编码格式,防止中文乱码
                String pageEncode = getEncode(responseHead);
                if(pageEncode != null 
                        && pageEncode.trim().length() > 0){
                    this.setPageEncode(pageEncode);
                    encode = pageEncode;
                }
                //6.读取网页源代码
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(
                                new GZIPInputStream(conn.getInputStream()),
                                encode));
                String line = "";
                while((line = in.readLine()) != null){
                    sb.append(line);
                    sb.append(System.getProperty("line.separator"));
                }
                in.close();                     
            }else{
                System.out.println("服务器响应失败,响应码:"+responseCode);
            }       
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }   
        return sb.toString();
    }

    private String getEncode(Map<String, List<String>> map) {
        String encode = "";
        Set<Map.Entry<String, List<String>>> entry = map.entrySet();
        for(Map.Entry<String, List<String>> temp: entry){
            if(temp.getKey() !=null&&
            temp.getKey().equals("Content-Type")){
                String value = temp.getValue().toString()
                        .replace("[", "").replace("]", "");
                int start = value.indexOf("=");
                if(start != -1){
                    encode = value.substring(start+1);
                }
            }
        }
        return encode;
    }

    private void printHead(Map<String, List<String>> map) {
        Set<Map.Entry<String, List<String>>> entry = map.entrySet();
        for(Map.Entry<String, List<String>> temp: entry){
            System.out.println(temp.getKey()+":"+temp.getValue());
        }
    }

    public String getPageEnCode() {
        return pageEnCode;
    }

    public void setPageEncode(String pageEnCode) {
        this.pageEnCode = pageEnCode;
    }

}

实验说明

设置请求头的时候,最好设置Accept-Encoding属性,如果不设置这个属性,一般网站不会发送压缩数据,但是也有一部分网站会发送压缩数据,这大概是网站服务器的默认属性,如果有一部分是压缩有一部分不压缩,就很难处理流了,所以我设置了这个属性,网站的服务器就会发送压缩数据,这样处理流数据的方式就统一了。PS:浏览器请求头设置Accept-Encoding属性也只是建议性的!!!

处理网页数据流时,一般用UTF-8解码就可以了,但是不能排除有一部分网站是以GBK或gb2312格式编码正文内容的。不以正确的格式读取流数据,就会得到乱码。获取网页正文编码方式的途径有两种:1.返回的请求头中Content-Type属性的值中可能包含编码方式(Content-Type:[text/html;charset=UTF-8])2.如果1中没有写编码格式,就只能从meta标签中读取了。

建议先用第一种方法获取编码格式。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐