获取天气预报乱码处理

因为业务需求需要获取获取天气信息,在获取天气信息遇到n多问题(感觉没爱了),以下是问题及解决

1.url关闭

之前获取天气预报的url :http://m.weather.com.cn/data/101110101.html  直接gg了,所以只能找其他的url代替了,找了半天找到了它

http://wthrcdn.etouch.cn/weather_mini?citykey=xxxxx  后面的xxxx指的是城市id,那么这个城市id怎么获取呢,

继续找找找了这个url:http://cj.weather.com.cn/support/Detail.aspx?id=51837fba1b35fe0f8411b6df这里面我们可以找打很多的id号,但是这么多的id不至于我们一个一个手动输入我们的数据库吧,答案当然不是,这个时候我们就可以使用我们强大的htmlunit网页抓取工具把我们想要的信息抓取出来,然后进行字符串分割保存到数据库中,具体如何抓取代码如下:

public static List<City> getCityInfo(){
        //设置htmlunit不打印日志
        LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog"); 
        WebClient webClient = new WebClient(BrowserVersion.CHROME);
        webClient.getOptions().setThrowExceptionOnScriptError(false);
        webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
        
        webClient.getOptions().setJavaScriptEnabled(true);
        webClient.getOptions().setActiveXNative(false);
        webClient.getOptions().setCssEnabled(false);
        webClient.getOptions().setThrowExceptionOnScriptError(false);

        webClient.setAjaxController(new NicelyResynchronizingAjaxController());

        webClient.getOptions().setJavaScriptEnabled(true);
        
        List<City> result = new ArrayList<City>();
        try {
            //获取城市id的url
            String url = "http://cj.weather.com.cn/support/Detail.aspx?id=51837fba1b35fe0f8411b6df";
            
            //通过webclient模拟一个网页,当然模拟的是这个url的
            HtmlPage page = webClient.getPage(url);
            
            //可以通过浏览器看到信息是放在p节点里的,所以我们获取所有的p节点
            DomNodeList<DomElement> list = page.getElementsByTagName("p");
          
            //遍历元素
            for(DomElement domElement : list){
                //这句话是为了过滤多余的标题和结尾信息
                String attr = domElement.getAttribute("style");
                
                //这句话是为了过滤第一行数据(city 城市 二级 一级)
                //如果child是0说明没有子节点 如果不为0说明有子节点那就是第一行的p节点这个不是我们需要的
                //可以通过浏览器查看p元素的结构
                int child = domElement.getChildElementCount();
                if(attr.equals("text-align:left;") && (child == 0)){
                    String res = domElement.getTextContent();
                    
                    //这里其实有个坑 在获取的第一行数据中后面2个逗号为中文的,其他的逗号都是英文的
                    //所以这里统一下全部改成中文
                    String a = res.replaceAll(",", ",");
                    
                    String []cityinfo = a.split(",");
                    City city = new City();
                    city.setId(cityinfo[0]);
                    city.setName(cityinfo[1]);
                    city.setSecgrade(cityinfo[2]);
                    city.setOndgrade(cityinfo[3]);
                    
                    result.add(city);
                }                
            }
            
            
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return result;
    }

这个方法可以写到一个工具类里面直接获取list就行了,不过有一个city  model需要你去建,拿到list后就可以保存到数据库中,需要一点时间毕竟有2275条数据^_^

 

2.拿到城市id 和获取天气的url(http://wthrcdn.etouch.cn/weather_mini?citykey=xxxxx )后获取的数据总是乱码

这个问题是使用最纠结的,本来工具类是写好get方法获取数据的但是每次都是获取乱码,不论是给输入流加了"utf-8"编码还是使用URLDecode进行解码全部无济于事,人家仍是安安全全的给你返回乱码代码如下:

public static String sendGet(String url,String param){
        URL realUrl = null;
        //PrintWriter out = null;
        String response = "";
        BufferedReader br = null;
        try {
            //真实地址
            realUrl = new URL(url);
            
            //打开连接
            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
            
            //设置连接属性            
            connection.setRequestProperty("accept", "application/xhtml+xml,application/json,application/xml;charset=UTF-8, text/javascript, */*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko");

//            connection.setDoOutput(true);
//            connection.setDoInput(true);
            
//            out = new PrintWriter(connection.getOutputStream());
//            out.write(param);
           
            //out.flush();
            
            br = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));
    
            String line = "";
            
            while((line=br.readLine())!=null){
                response +=line;
            }
            
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
                
        }finally{
            
            //out.close();
            
            try {
                br.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
        return response;
    }
    

网上也是有很多人推荐的是这种方法,but不行,纠结痛苦半天后我想到wireshark抓包工具,于是打开之,开始抓包

获取到抓包数据了 在我们的需要的数据上右击追踪流

 

然后我们就可以看见返回的数据信心,然后我们就会发现一个大坑,那就是返回的内容类型content-type:gzip,浏览器可以自动解析这个格式but我们java客户端不会呀,这也是为什么当你把url直接输入到浏览器可以看到完美的返回数据,但是从java http客户端获取就是乱码的原因

 

于是就想办法解决如何读取gzip压缩格式的文件,哈哈哈,还好sun公司已经封装了这个读取流^_^直接上代码:

/**
     * 获取天气预报信息
     * @throws UnsupportedEncodingException 
     */
    public static String getWeather(String url) throws UnsupportedEncodingException{
    
        URL realUrl = null;
        ByteArrayOutputStream out = null;
        
        try {
            //真实地址
            realUrl = new URL(url);
            
            //打开连接
            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
            
            //设置连接属性            
            connection.setRequestProperty("accept", "application/xhtml+xml,application/json,application/xml;charset=utf-8, text/javascript, */*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("contentType", "utf-8");
            connection.setRequestMethod("GET");
            connection.setRequestProperty("user-agent",
                    "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko");

            //FileOutputStream out = new FileOutputStream("e:/text.txt");
            //这里获取的数据时压缩格式的数据所以用gzip进行解压缩
            GZIPInputStream gip = new GZIPInputStream(connection.getInputStream());
            out = new ByteArrayOutputStream();
            //缓冲
            byte []buffer = new byte[1024];
            int len ;
            while((len = gip.read(buffer))!=-1){
                out.write(buffer, 0, len);
            }        
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
                
        }finally{
            //关闭流
            try {
                if(out != null){
                    out.close();
                }                
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
        //把字节数据转化为字符串返回回去
        return (new String(out.toByteArray(), "utf-8"));
    }

同样这个方法写到一个工具类中就可以直接使用^_^

整个问题的解决感觉最坑的一步就是url返回的是gzip格式的压缩文件而不是我们希望的json数据 大坑~,

到此结束^_^

 

posted @ 2016-04-26 15:36  hotlove_linx  阅读(4409)  评论(0编辑  收藏  举报