因为业务需求需要获取获取天气信息,在获取天气信息遇到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数据 大坑~,
到此结束^_^