Android访问中央气象台的天气预报API得到天气数据

最新说明:该接口已失效! 2014-03-04    


可申请它公布的API,需申请:http://smart.weather.com.cn/wzfw/smart/weatherapi.shtml

在用Android获取天气预报数据时,大家一定会首先想到Google的天气预报API,其实除了Google的天气预报API,免费的天气预报接口还有http://www.webservicex.net/globalweather.asmx?op=GetWeather、http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx和中央气象台的天气预报,这三个是我最近试过的都可以访问,网上其实还介绍的有www.ayandy.com,不过我没试过 ^_^

     现在就来谈一谈这几个服务,google的就不说了,www.webservicex.net的这个是一个国外的,获取中国国内的有点麻烦,且只有当天的天气,所以果断放弃...呵呵

而webservice.webxml.com.cn的这个确实不错,访问的数据是来自中国气象局http://www.cma.gov.cn/ 数据的准确就不用说了,但他分为付费和免费的,免费的服务有点不稳定,我就曾遇到一回,所以也不是很好,最后就只剩下中央气象台的天气预报的API了,这个我不想说太多,虽然获取时你首先要知道对应的城市码,有点麻烦,其它的如稳定性与广阔性也是很一流的,它可以精确到县和区.下面就直奔主题:

这个服务的天气预报的请求地址是:http://m.weather.com.cn/data/101070201.html,这个文本就是城市天气URL,101070201代表的为对应地区的编码,执行URL,得到一个返回文本,是JSON格式的,如下(经过格式化):

{ 
    "weatherinfo":{
                     "city":"成都",
                     "city_en":"chengdu",
                     "date_y":"2011年11月30日",
                     "date":"辛卯年",
                     "week":"星期三",
                     "fchh":"11",           //预报发布时间
                     "cityid":"101270101",
                     "temp1":"13℃~10℃",
                     "temp2":"14℃~6℃",
                     "temp3":"13℃~5℃",
                     "temp4":"14℃~8℃",
                     "temp5":"10℃~8℃",
                     "temp6":"11℃~6℃",
                     "tempF1":"55.4℉~50℉",
                     "tempF2":"57.2℉~42.8℉",
                     "tempF3":"55.4℉~41℉",
                     "tempF4":"57.2℉~46.4℉",
                     "tempF5":"50℉~46.4℉",
                     "tempF6":"51.8℉~42.8℉",
                     "weather1":"阴转多云",
                     "weather2":"多云转晴",
                     "weather3":"多云转阴",
                     "weather4":"阵雨",
                     "weather5":"阵雨转小雨",
                     "weather6":"小雨转阴",
                     "img1":"2",
                     "img2":"1",
                     "img3":"1",
                     "img4":"0",
                     "img5":"1",
                     "img6":"2",
                     "img7":"3",
                     "img8":"99",
                     "img9":"3",
                     "img10":"7",
                     "img11":"7",
                     "img12":"2",
                     "img_single":"2",
                     "img_title1":"阴",
                     "img_title2":"多云",
                     "img_title3":"多云",
                     "img_title4":"晴",
                     "img_title5":"多云",
                     "img_title6":"阴",
                     "img_title7":"阵雨",
                     "img_title8":"阵雨",
                     "img_title9":"阵雨",
                     "img_title10":"小雨",
                     "img_title11":"小雨",
                     "img_title12":"阴",
                     "img_title_single":"阴",
                     "wind1":"北风小于3级",
                     "wind2":"北风小于3级",
                     "wind3":"北风小于3级",
                     "wind4":"南风转北风小于3级",
                     "wind5":"北风小于3级",
                     "wind6":"北风小于3级",
                     "fx1":"北风",
                     "fx2":"北风",
                     "fl1":"小于3级",
                     "fl2":"小于3级",
                     "fl3":"小于3级",
                     "fl4":"小于3级",
                     "fl5":"小于3级",
                     "fl6":"小于3级",
                     "index":"舒适",
                     "index_d":"建议着薄型套装或牛仔衫裤等春秋过渡装。年老体弱者宜着套装、夹克衫等。",
                     "index48":"舒适",
                     "index48_d":"建议着薄型套装或牛仔衫裤等春秋过渡装。年老体弱者宜着套装、夹克衫等。",
                     "index_uv":"最弱",
                     "index48_uv":"弱",
                     "index_xc":"适宜",
                     "index_tr":"很适宜",
                     "index_co":"较舒适",
                     "st1":"14",
                     "st2":"10",
                     "st3":"14",
                     "st4":"6",
                     "st5":"13",
                     "st6":"4",
                     "index_cl":"较适宜",
                     "index_ls":"不太适宜",
                     "index_ag":"较易发"
        }
}
上面显示的信息一目了然,就不用我多说了(在得到天气数据的解析android2.2可用JSONObject对象参考: http://henzil.easymorse.com/?p=242),关键是获得对应地区的城市码了。

访问http://m.weather.com.cn/data5/city.xml得到一级列表(省、直辖市、自治区),结果用逗号隔开,id和城市名称使用竖线“|”隔开;结果示例如下:

01|北京,02|上海,03|天津,04|重庆,05|黑龙江,06|吉林,07|辽宁,08|内蒙古,09|河北,10|山西,11|陕西,.....

然后通过这一次访问得到的一级码得到二级码,如查找河北的:http://m.weather.com.cn/data5/city09.xml在city后加上一级码得到:

0901|石家庄,0902|保定,0903|张家口,0904|承德,0905|唐山,0906|廊坊,0907|沧州,0908|衡水,0909|邢台,0910|邯郸,0911|秦皇岛

然后再通过得到的二级码得到三级的县或区的编码:如唐山:http://m.weather.com.cn/data5/city0905.xml:

090501|唐山,090502|丰南,090503|丰润,090504|滦县,090505|滦南,090506|乐亭,090507|迁西,090508|玉田,090509|唐海,090510|遵化,090511|迁安
最后由得到了三级码就可能得到它对应的城市码了,如访问唐山.迁西:http://m.weather.com.cn/data5/city090507.xml得到:

090507|101090507

则可知河北.唐山.迁西的城市编码为:101090507,得到它的天气则通过访问http://m.weather.com.cn/data/101090507.html得到。

    好了,相信通过上面的叙述,大家应该知道怎么得到一个城市的天气了;但接下来问题又来了,那我们怎样通过Android访问来得到全国任意一个地区的天气呢?难道每一次访问网络三次得到城市码,再用它访问m.weather.com.cn这个网址得到天气码?天啊?这怎么行!要知道我们手中的移动设备可不像电脑那样"富有",流量是要扣钱的!!

   相信计算机算法好的同鞋已经想到了,就是通过程序将有的城市对应的天气码一次访问到本地用android上的数据库保存起来,这样在每一次需要时,只需查询一下数据库就可以了,那关键就是怎样遍历得到所有的城市码,现给出本人的遍历代码(来自于本人天气预报系统):

        //一个自定义的网络访问工具类
        WebAccessTools webTools = new WebAccessTools(this);  //这里的this为context对象
        //得到访问网络的内容
        String webContent=webTools.getWebContent("http://m.weather.com.cn/data5/city.xml");
        
        //第一次解析得到的为省份或一级直辖市
        String[][] provinces = WeaterInfoParser.parseCity(webContent);  //WeatherInfoParser为自定义的一个解析字符串类
        String[] groups = new String[provinces.length];
        String[][] childs = new String[provinces.length][];
        String[][] cityCode = new String[provinces.length][];
        for(int i=0; i< provinces.length; i++) {
        	groups[i] = provinces[i][1];
        	//由省份码来得到城市码
        	StringBuffer urlBuilder= new StringBuffer("http://m.weather.com.cn/data5/city");
        	urlBuilder.append(provinces[i][0]);
        	urlBuilder.append(".xml");
        	webContent = webTools.getWebContent(urlBuilder.toString());
        	String[][] citys = WeaterInfoParser.parseCity(webContent);
        	//用于保存所的有二级市对应的towns县区
        	String[][][] towns = new String[citys.length][][];
        	//计算总的城镇数
        	int sum=0;
        	for(int j=0; j<citys.length; j++) {
        		//由城市码来得到地方码
        		urlBuilder= new StringBuffer("http://m.weather.com.cn/data5/city");
        		urlBuilder.append(citys[j][0]);
        		urlBuilder.append(".xml");
        		webContent = webTools.getWebContent(urlBuilder.toString());
        		towns[j] = WeaterInfoParser.parseCity(webContent);
        		sum = sum + towns[j].length;
        	}
        	
        	childs[i] = new String[sum];
        	cityCode[i] = new String[sum];
        	
        	sum=0;
        	for(int j=0; j<citys.length; j++) {
        		for(int n=0; n<towns[j].length; n++) {
        			if(n==0)
        				childs[i][sum] = towns[j][n][1];
        			else
        				childs[i][sum] = towns[j][0][1] + "." + towns[j][n][1];
        			
        			urlBuilder= new StringBuffer("http://m.weather.com.cn/data5/city");
        			urlBuilder.append(towns[j][n][0]);
        			urlBuilder.append(".xml");
        			
        			webContent = webTools.getWebContent(urlBuilder.toString());
        			String[][] code=WeaterInfoParser.parseCity(webContent);
        			cityCode[i][sum] = code[0][1];
        			sum = sum + 1;
        		}
        	}
        	urlBuilder=null;
        }
        
        //=======这里得到的groups数组记录的是得到的34个一级地区字符串 =====================
        //=======childs记录的是与groups数组对应的一级地区对应的市级别或县区的字符串名,如:上海.闵行的形式(上海市没有.为上海)===
        //=======cityCode和childs形式相同,只不过记录的为地区的城市码=====================
        //=======下面就是将得到的城市码保存到数据库中,这里建了二张表,省份表和城市表,城市表通过province_id与省份表关联===========
        //============================Create Database================================
        //打开或创建一个数据库,数据库路径为:/data/data/包名/数据库文件
        String path="/data"+ Environment.getDataDirectory().getAbsolutePath() + "/com.weather.app/db_weather.db";
        
        SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase
                                  (path, null);
        //创建一个省份表
        String sql="create table provinces (_id integer primary key autoincrement, name text)";
        database.execSQL(sql);
        
        //创建城市表
        sql = "create table citys (_id integer primary key autoincrement, province_id integer, name text, city_num text)";
        database.execSQL(sql);
        
        //插入省份数据
        ContentValues cv = null;
        for(int i=0; i<provinces.length; i++) {
        	cv = new ContentValues();
        	cv.put("name", provinces[i][1]);
        	database.insert("provinces", null, cv);
        }
        //插入城市数据
        for(int i=0; i<childs.length; i++) {
        	for(int j=0; j<childs[i].length; j++) {
        		cv = new ContentValues();
        		cv.put("province_id", i);
        		cv.put("name", childs[i][j]);
        		cv.put("city_num", cityCode[i][j]);
        		database.insert("citys", null, cv);
        	}
        }
        cv = null;
        database.close();
下面给出WebAccessTools工具类:

public class WebAccessTools {
	
	/**
	 * 当前的Context上下文对象
	 */
	private Context context;
	/**
	 * 构造一个网站访问工具类
	 * @param context 记录当前Activity中的Context上下文对象
	 */
	public WebAccessTools(Context context) {
		this.context = context;
	}
	
	/**
	 * 根据给定的url地址访问网络,得到响应内容(这里为GET方式访问)
	 * @param url 指定的url地址
	 * @return web服务器响应的内容,为<code>String</code>类型,当访问失败时,返回为null
	 */
	public  String getWebContent(String url) {
		//创建一个http请求对象
		HttpGet request = new HttpGet(url);
		//创建HttpParams以用来设置HTTP参数
		HttpParams params=new BasicHttpParams();
		//设置连接超时或响应超时
		HttpConnectionParams.setConnectionTimeout(params, 3000);
		HttpConnectionParams.setSoTimeout(params, 5000);
		//创建一个网络访问处理对象
		HttpClient httpClient = new DefaultHttpClient(params);
		try{
			//执行请求参数项
			HttpResponse response = httpClient.execute(request);
			//判断是否请求成功
			if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				//获得响应信息
				String content = EntityUtils.toString(response.getEntity());
				return content;
			} else {
				//网连接失败,使用Toast显示提示信息
				Toast.makeText(context, "网络访问失败,请检查您机器的联网设备!", Toast.LENGTH_LONG).show();
			}
			
		}catch(Exception e) {
			e.printStackTrace();
		} finally {
			//释放网络连接资源
			httpClient.getConnectionManager().shutdown();
		}
		return null;
	}
}
就一个方法.

下面的解析工具类也只一个方法:

public class WeaterInfoParser {
	
	/**
	 * 通过解析content,得到一个一维为城市编号,二维为城市名的二维数组
	 * 解析的字符串的形式为: <code>编号|城市名,编号|城市名,.....</code>
	 * @param content 需要解析的字符串
	 * @return 封装有城市编码与名称的二维数组
	 */
	public static String[][] parseCity(String content) {
		//判断content不为空
		if(content!=null&&content.trim().length()!=0) {
			StringTokenizer st=new StringTokenizer(content, ",");
			int count = st.countTokens();
			String[][] citys = new String[count][2];
			int i=0, index=0;
			while(st.hasMoreTokens()) {
				String city = st.nextToken();
				index = city.indexOf('|');
				citys[i][0] = city.substring(0, index);
				citys[i][1] = city.substring(index+1);
				i = i+1;
			}
			return citys;
		}
		return null;
	}
}

如上,这样就得到了国内所有地区的城市码数据库文件了,上面的方法在模拟器中运行时很慢,数分钟才能出来结果,要有耐心^_^!!

其通过上面的代码我们得到最重要的是db_weather.db这个文件,在Android中我们只要得到了它就可在程序第一次运行时直接导入到/data/data/包名/databases目录中就行了,已后直接调用,

关于数据库的导入,实质上就是文件的复制,我们只需在将数据库文件放在程序包中的res/raw目录中在运行时复制到databases目录是就行了,下面同样给出导入代码:

//将res/raw中的城市数据库导入到安装的程序中的database目录下
    public void importInitDatabase() {
    	//数据库的目录
    	String dirPath="/data/data/com.weather.app/databases";
    	File dir = new File(dirPath);
    	if(!dir.exists()) {
    		dir.mkdir();
    	}
    	//数据库文件
    	File dbfile = new File(dir, "db_weather.db");
    	try {
    		if(!dbfile.exists()) {
    			dbfile.createNewFile();
    		}
    		//加载欲导入的数据库
    		InputStream is = this.getApplicationContext().getResources().openRawResource(R.raw.db_weather);
    		FileOutputStream fos = new FileOutputStream(dbfile);
    		byte[] buffere=new byte[is.available()];
    		is.read(buffere);
    		fos.write(buffere);
    		is.close();
    		fos.close();

    	}catch(FileNotFoundException  e){
    		e.printStackTrace();
    	}catch(IOException e) {
    		e.printStackTrace();
    	}
    }


我得到的城市码数据库文件可到http://download.csdn.net/detail/xianqiang1/3896880下载


上面的是我从天气预报系统中截取的部分代码,如有错误,欢迎留言指出!!

参照文章:使用JACKSON解析JSON(HttpClient 3处理请求)http://sarin.iteye.com/blog/821534

声明:感谢summerxzg和kpiao,指出了数据库文件的错误:我使用sqlitebrowser工具查看了一下我生成的db文件,显示出的的城市码数为2566个而summerxzg指出的为2583条,我的为不全。kpiao指出的citys表中的province_id与provinces表中的id不对应是由于代码中的是citys表创建时province_id是由0开始,而provinces表中的自动增长是从1开始故存在不匹配问题。为了准确起见,大家还是自己遍历一个数据库,小子我由于近期较忙暂时不会对上面内容作更改{^_^},再次感谢你们!

现在公布源码及文档下载地址:http://download.csdn.net/detail/xianqiang1/4535734

关于大家说的图片对不上,应该可以参考:http://www.weather.com.cn/static/html/legend.shtml

                      

注意:推荐参考一下33楼的改进,感谢softwarehe的分享。

已标记关键词 清除标记
相关推荐
Discuz! X2.5新版本推出四大新体系:新社区形态、新技术体系、新门户系统以及新移动化体验,其中新门户系统主要从内容推送、图片裁切、模块管理等角度进行了深度优化,拟帮助站长和编辑更加省时省力地运维网站,提高网站运营管理效率。 Discuz! X2.5重要改变: 产品架构全新改造 研发团队集中数月的时间,一直在致力于改造Discuz!产品的现有架构,仅数据库操作相关的架构优化,就涉及317个程序文件的4525处代码片段修改,并新增DB类文件217个。 从Discuz! X2.5起,数据读写层已经实现全面独立封装。新版支持以表为单位的分服务器部署;数据表全面支持内存级缓存启用,大幅度降低MySQL压力;对数据的输入进行了统一防注射处理,加强数据入库安全性。 性能负载能力和稳定性大幅优化 针对大访问量情况下,收集到各类性能瓶颈点,新版进行了集中优化和改进,其中包括: 采用全新的帖子查看数更新机制,解决大访问量情况下,因瞬间大批量更新主题表造成的MySQL死锁的问题; 用户表支持内存级缓存启用;同时,新增不活跃用户(大部分站点比例超过70%)归档功能,让用户表只保留最近活跃用户,从而大幅度提升用户表性能; 高楼层贴不再是性能瓶颈。帖子表已经优化索引模式,启用全新的查询模式,浏览高楼层帖子的分页不再有负载问题; 对帖子中的评分、点评数据增加缓存功能,解决评分、点评功能大量使用可能造成PHP内存占用的问题; DIY模块聚合增加限制数目优化,避免复杂检索条件下对数据库造成全表遍历的负载问题; session机制新增关闭可选项。访问量大的网站可以考虑关闭session机制,增强系统抗压性能。 淘贴 为了更好的挖掘站点内容,形成历史沉淀,增强可阅读性,新增淘贴功能。通过此功能,可以借助会员的力量,让论坛的好帖子得到更好的整理与展现,让论坛会员可以更好的发现站点精彩内容。 微博元素 新增单向关注功能、@用户功能,让微博中的优秀元素为论坛所用;与论坛紧密结合的特色广播功能,同一个主题两种浏览模式,让传统的以不同版块为入口的阅读模式,可以平滑的切换到以收听关系链为主的新阅读体验,互动统一,相辅相成;增加腾讯微博评论回流功能,分享到微博的帖子,如果在微博中有人评价,也会自动回帖到论坛的帖子里面。 应用中心 新版后台内置应用中心。通过应用中心,站长寻找和安装插件、模板、扩展不再迷茫,实现一键安装。当插件有更新的时候,会自动提示进行升级,让插件的升级也变得十分简单和及时。 同时,面向PHP开发人员、UI设计人员推出Discuz!开发者平台,可以更方便的发布和维护自己的插件、扩展产品和模板风格。 安全补丁和自动升级 新增全新的安全补丁更新和产品自动升级机制。当产品发现重要安全问题的时候,站长将会收到安全警示,并实现在线打补丁和版本更新功能。同时,考虑到很多站点都进行了自我功能开发,源文件有所修改,因此,打补丁启用了独有的字符串匹配替换模式,可以在不影响站点自己定制功能情况下,尽大可能的进行漏洞修补。 站长管理 新版管理后台,进行了多达25处的功能改进,让站长的管理更加方便。重点包括用户批量管理方面的使用改进;主题、回帖回收站的批量删除改进;进行批量编辑设置时管理优化;管理日志查询功能增强;优化表情添加操作,批量添加表情更方便;增强嵌入钩子检测;站长推荐功能增强,可以添加多条推荐内容,前台会随机显示1条;创建导航优化;增加外地ip发帖审核机制;快速清除某人签名、自定义头衔、头像等。 论坛功能 新版针对论坛相关的功能,有接近40项的功能改进,让用户使用论坛的体验进一步增强。其中包括:用户收藏的版块在论坛首页优先显示,更方便的访问自己收藏的论坛;增加定时发帖功能;增加隐藏帖子代码的时间设置,到期后隐藏内容自动失效;帖子内容页图片幻灯支持网络图片,同时也帖子封面图支持远程图片,并在帖子列表图片模式插件嵌入点;帖子摄影图片增加exif信息;优化上传功能,Flash批量上传全新改造;新版新增给用户打标签的功能,站长可以设置一些触发条件,当用户参与后,就会自动设置一个标签,从而可以精确实现对站内用户的画像;针对用户的管理,增加用户违规记录档案,让多次违规用户可以更好被跟踪;新增全新注册流程模式,用户需要先验证邮箱然后再可以进行下一步注册操作,以有效防止大量垃圾注册的产生;新增导读功能,将近期最热、最新的主题更好的进行展示等。 分类信息 新版针对分类信息进行了功能增强,其中新增加字段导入导出的功能,可以让分类信息的模型分享变得更加开放和方便;显示模板已经支持完全自定义,从而让分类信息在前台的展示,更加自由,不再拘束于现有版块的布局约束。此外,还包括增强对分类信息数值型、Email、URL的检测;TEXTAREA类型的字段也可以使用图片、HTML加密,以及用户组权限分配;字段的信息保护增加认证组设置等。 群组 新版针对群组增加了先建立后审核功能,可以更好的规范群组的创建;群组已经允许在一级分类创建;群组简介支持Discuz! code等功能改进等 门户 新版针对门户功能,有20多项的功能改进,重点包括:增加编辑人员推送模块权限,编辑在前台有DIY的权限,但只能编辑模块数据;推送数据时可以读取多张图片供选择并支持在线剪裁图片;文章图片上传方式改进;优化门户推送功能,并加强推送库管理;生成文章时,可以将当前帖子楼主的所有楼层生成一篇文章;生成文章流程进行整体优化;专题评论增加引用功能等。 Discuz! X2.5 正式版 build 20130222更新记录: IX 由于部分变量过滤不严导致的XSS问题 FIX 管理员在版块前台管理面板中更新推荐主题时,原主题推荐者ID被修改为管理员ID的问题(14039) FIX soso表情更换新域名
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页