因為業務需求需要獲取獲取天氣信息,在獲取天氣信息遇到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數據 大坑~,
到此結束^_^
