Java学习之利用yahoo weather api 获取天气预报

在cnblogs看到 狼の禅 写的Java获取yahoo天气预报一文,于是有了这篇文章。
狼の禅 是将<地点代码,地名> 对用HashMap 来保存在类中的,发现手动查找地点代码然后要一个个加进去还真麻烦,于是想下有没有偷懒的办法。果然,在雅虎 geoplanet 找到了答案。
最简单的办法是申请一个yahoo dev 的key ,然后就可以通过 GeoPlanet api 来查询相应地点的WOEID了。
狼の禅 用的是旧的p 参数传递地点代码,不过最新的api文档里面只对w参数作了说明,因此这里我就用WOEID了。
不想注册申请key,还是自己折腾吧。
下载最新的 GeoPlanet Data ,解压出来有这些个文件:

The zip file below contains a license file, a readme file, and three data files in tab-delineated, Unicode (UTF-8) format:
geoplanet_places_[version].tsv: the WOEID, the placename, and the WOEID of its parent entity
geoplanet_aliases_[version].tsv: alternate names in multiple languages indexed against the WOEID
geoplanet_adjacencies_[version].tsv: the entities neighboring each WOEID
geoplanet_changes_[version].tsv: the list of removed WOEIDs and their replacement WOEID mappings

这里我只取 geoplanet_places_7.6.0.tsv ,用EmEditor 打开,把 中国 地区的 COPY 出到另外一个文件
Chinaplaces.tsv .
这个tsv 文件是用tab分隔字段的,places文件的字段有:

WOE_ID ISO Name Language PlaceType Parent_ID

直接解析这个tsv文件不怎么方便,还是用MySQL来搞定它吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE USER 'geoplanet'@'localhost' IDENTIFIED BY 'geoplanet';

GRANT USAGE ON * . * TO 'geoplanet'@'localhost' IDENTIFIED BY 'geoplanet' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0 ;

CREATE DATABASE IF NOT EXISTS `geoplanet` ;

ALTER DATABASE `geoplanet` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

GRANT ALL PRIVILEGES ON `geoplanet` . * TO 'geoplanet'@'localhost';

FLUSH PRIVILEGES;

CREATE TABLE IF NOT EXISTS `places` (
  `woe_id` varchar(15) NOT NULL,
  `iso` varchar(6) NOT NULL,
  `name` text NOT NULL,
  `language` varchar(6) NOT NULL,
  `placetype` varchar(15) NOT NULL,
  `parent__id` varchar(15) NOT NULL,
  PRIMARY KEY (`woe_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

然后,导入之( geoplanet 为数据库名):

1
mysqlimport --ignore-lines=1 --fields-optionally-enclosed-by='"' --fields-terminated-by="\t"  --user="root" --password --local geoplanet places.tsv

或者直接进mysql:

1
2
use geoplanet;
LOAD DATA LOCAL INFILE 'D:\\code\\java\\geoplanet_data_7.6.0\\Chinaplaces.tsv' INTO TABLE `places` FIELDS TERMINATED BY '\t' ENCLOSED BY '"' ESCAPED BY '\' LINES TERMINATED BY '\n' IGNORE 1 LINES;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> use geoplanet
Database changed
mysql> show tables;
+---------------------+
| Tables_in_geoplanet |
+---------------------+
| places              |
+---------------------+
1 row in set (0.00 sec)

mysql> LOAD DATA LOCAL INFILE 'D:\\code\\java\\geoplanet_data_7.6.0\\Chinaplaces
.tsv'
INTO TABLE `places` FIELDS TERMINATED BY '\t' ENCLOSED BY '"' ESCAPED BY '
\'
LINES TERMINATED BY '\n' IGNORE 1 LINES;
Query OK, 10519 rows affected (0.26 sec)
Records: 10519  Deleted: 0  Skipped: 0  Warnings: 0

然后就是连接mysql啦(java目前我也就写过一个hello world, class写得勉强,见笑了 :smile: ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package com.ihacklog.java.learn;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;

/**
 * MySQL辅助类
 * @author HuangYe <荒野无灯>
 * @URL http://ihacklog.com
 * @see http://dev.mysql.com/doc/refman/5.5/en/connector-j.html
 */


public class mysql {
    private String server = "localhost";
    private String port = "3306";
    private String user = "geoplanet";
    private String password = "geoplanet";
    private String database = "geoplanet";
    private static Connection conn = null;
    private String query = null;

    public mysql(String server, String port, String user, String password,
            String database) {
        super();
        this.server = server;
        this.port = port;
        this.user = user;
        this.password = password;
        this.database = database;

        try {
            // The newInstance() call is a work around for some
            // broken Java implementations

            Class.forName("com.mysql.jdbc.Driver").newInstance();
            this.connect();
        } catch (Exception ex) {
            // handle the error
        }

    }

    /**
     * create JDBC connection
     *
     * @return Connection
     */

    public Connection connect() {
        if (null == mysql.conn) {
            try {
                mysql.conn = DriverManager.getConnection("jdbc:mysql://"
                        + this.server + ":" + this.port + "/" + this.database
                        + "?" + "user=" + this.user + "&#038;password="
                        + this.password + "&#038;characterEncoding=utf8");

            } catch (SQLException ex) {
                // handle any errors
                System.out.println("SQLException: " + ex.getMessage());
                System.out.println("SQLState: " + ex.getSQLState());
                System.out.println("VendorError: " + ex.getErrorCode());
            }
        }
        return mysql.conn;
    }


    public String getOneCol(String query,String columnName)
    {
        Statement stmt = null;
        ResultSet rs = null;
        String colString ="";
        this.query = query;
        try{
        stmt = mysql.conn.createStatement();
        rs = stmt.executeQuery(this.query);
        while(rs.next()) {
            colString = rs.getString(columnName);
        }
        }
         catch (SQLException sqlEx) {
            } // ignore

        finally {

            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException ex) {
                    // ignore
                }
            }

            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException ex) {
                    // ignore
                }
            }
        }
         return colString;
    }
}

然后是天气信息查询类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package com.ihacklog.java.learn;
//For saving XML file
/*import java.io.File;
import java.io.FileOutputStream;*/

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
//For saving XML file
/*import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;*/

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.ihacklog.java.learn.mysql;

/**
 * @author 狼の禅 [http://www.cnblogs.com/kingwolfofsky], HuangYe <荒野无灯>
 *
 */

class GetWeatherInfo {
    private static mysql db= null;
    /**
     * 天气信息代码
     * @see http://developer.yahoo.com/weather/#codes
     */

    private final String[] dictionaryStrings = { "龙卷风", "热带风暴", "飓风", "强雷阵雨",
            "雷阵雨", "小雨加雪", "雨加冰雹", "雪加冰雹", "冰雨", "毛毛雨", "冻雨", "阵雨", "阵雨", "小雪",
            "零星小雪", "高吹雪", "雪", "冰雹", "雨夹雪", "尘", "雾", "薄雾", "多烟的", "大风", "有风",
            "寒冷", "阴天", "夜间阴天", "白天阴天", "夜间多云", "白天多云", "夜间清亮", "晴朗", "转晴",
            "转晴", "雨夹冰雹", "热", "雷阵雨", "雷阵雨", "雷阵雨", "雷阵雨", "大雪", "阵雪", "大雪",
            "多云", "雷", "阵雪", "雷雨" };
    public GetWeatherInfo() {
        GetWeatherInfo.db=new mysql("localhost", "3306", "geoplanet", "geoplanet", "geoplanet");
    }
 
    private String getCityWOEID(String cityName)
    {
        String woeid = null;
        woeid=GetWeatherInfo.db.getOneCol("SELECT woe_id FROM places WHERE name='"+ cityName + "'","woe_id");
        return woeid;
    }
    /**
     * modified by 荒野无灯
     * @see http://developer.yahoo.com/weather/
     * @see http://en.wikipedia.org/wiki/WOEID
     * @see http://developer.yahoo.com/geo/geoplanet/
     * @see http://developer.yahoo.com/geo/geoplanet/data/
     * @param cityCode
     * @return  Document
     * @throws IOException
     */

    private Document getWeatherXML(String cityCode) throws IOException {
        URL url = new URL("http://weather.yahooapis.com/forecastrss?w=" + cityCode + "&#038;u=c");
        URLConnection connection = url.openConnection();
        Document Doc = stringToElement(connection.getInputStream());
        return Doc;
    }
    /* 保存获取的天气信息XML文档 */
/*    private void saveXML(Document Doc, String Path) {
        TransformerFactory transFactory = TransformerFactory.newInstance();
        Transformer transformer;
        try {
            transformer = transFactory.newTransformer();
            DOMSource domSource = new DOMSource(Doc);
            File file = new File(Path);
            FileOutputStream out = new FileOutputStream(file);
            StreamResult xmlResult = new StreamResult(out);
            transformer.transform(domSource, xmlResult);
        } catch (Exception e) {
            System.out.println("保存文件出错!");
        }
    }*/

    /* 获取天气信息 */
    public String getWeather(String city) {
        String result = null;
        try {
            String cityWOEID =getCityWOEID(city);
            if( null == cityWOEID )
                return null;
           
            Document doc = getWeatherXML( cityWOEID );
            NodeList nodeList = doc.getElementsByTagName("channel");
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                NodeList nodeList1 = node.getChildNodes();
                for (int j = 0; j < nodeList1.getLength(); j++) {
                    Node node1 = nodeList1.item(j);
                    if (node1.getNodeName().equalsIgnoreCase("item")) {
                        NodeList nodeList2 = node1.getChildNodes();
                        for (int k = 0; k < nodeList2.getLength(); k++) {
                            Node node2 = nodeList2.item(k);
                            if (node2.getNodeName().equalsIgnoreCase(
                                    "yweather:forecast")) {
                                NamedNodeMap nodeMap = node2.getAttributes();
                                Node lowNode = nodeMap.getNamedItem("low");
                                Node highNode = nodeMap.getNamedItem("high");
                                Node codeNode = nodeMap.getNamedItem("code");
                                String day = "\n明天";
                                if (result == null) {
                                    result = "城市:" + city ;
                                    day = "\n今天";
                                } else {
                                    day = "\n明天";
                                }
                                result = result
                                        + day
                                        + " "
                                        + dictionaryStrings[Integer
                                                .parseInt(codeNode
                                                        .getNodeValue())]
                                        + "\t最低温度:" + lowNode.getNodeValue()
                                        + "℃ \t最高温度:" + highNode.getNodeValue()
                                        + "℃ ";
                            }
                        }
                    }
                }
            }
            //save file JUST for DEBUG
//            saveXML(doc, "D:\\tmp\\Weather.xml");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    public Document stringToElement(InputStream input) {
        try {
            DocumentBuilder db = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder();
            Document doc = db.parse(input);
            return doc;
        } catch (Exception e) {
            return null;
        }
    }
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ihacklog.java.learn;
import com.ihacklog.java.learn.GetWeatherInfo;


/**
 * @author HuangYe <荒野无灯>
 * @URL http://ihacklog.com
 */

public class getWeather {
    public static void main(String arg[]) {
       
        //OK,let's begin the test.
        //Have fun!
        GetWeatherInfo info = new GetWeatherInfo();
        String weather = info.getWeather("岳阳市");
        System.out.println(weather);
       
        weather = info.getWeather("哈尔滨");
        System.out.println(weather);
       
        weather = info.getWeather("海口市");
        System.out.println(weather);
       
        weather = info.getWeather("长沙市");
        System.out.println(weather);
       
        weather = info.getWeather("武汉市");
        System.out.println(weather);
    }
}

本文参考文档:
Importing GeoPlanet data into MySQL
Java获取yahoo天气预报
JDBC and MySQL: Run the Programs
Yahoo! GeoPlanet Guide

Yahoo! GeoPlanet™ Data


Yahoo! Weather RSS Feed docs

更多
10 Responses Post a comment
  1. 金冈

    http://sugg.us.search.yahoo.net/gossip-gl-location/?appid=weather&output=xml&command=上海市

    查询的结果有好多个,一个地名可能有两三个woeid,但是经纬度却很相近,有些城市很大,有两三个woeid很正常,经纬度相近基本就是一个地方,查询时要是“市”加进去,这样查询的结果准度比较高,我都是直接选取第一个。

    yahoo貌似还提供个一个通过经纬度获取woeid的API,不过要获取客户端的地理位置了。

  2. 荒野无灯

    @绿茶这个自从我重装过N次系统后已经不知去向了。你可以自己去yahoo网站下载。

  3. 绿茶

    博主,给我也来一份中国区的WOEID吧。。这东西靠应用程序自己查太恶心了。因为HTML5浏览器还得提示用户那个获取地理位置信息的确认框

  4. 荒野无灯

    呵呵,好像目前免费提供天气API的也就yahoo 了。

  5. Neeke

    :!: yahoo有天气API啊???前天我还在到处找呢...

  6. 荒野无灯

    如果要想查到100%正确的woeid,可以考虑用yahoo的 geoplanet 服务。

  7. 荒野无灯

    事实上,yahoo官方是结合几个数据一起的,我这里只是简单查询而已。就算是只查一个文件,结果也可能有多个,如 长沙 与 长沙市 ,同一个地方,woeid却不同。

  8. 代码回音

    收到文件,为何我发现文件里面的woeid值跟雅虎网站实际查出来地不一样呢,比如哈尔滨文件中是26198257,实际查找是harbin-2141166,因而会导致Yahoo! Weather - Error

  9. 荒野无灯

    是的,那个是全世界的。。。
    中国区也就450多KB而已。发你邮箱吧。

  10. 代码回音

    呵呵,有点意思啊,你可否把中国区的那个woedi文件发一份给我啊。我想下载的,105m呢。
    谢啦

Leave a Reply

Note: You may use basic HTML in your comments. Your email address will not be published.

Subscribe to this comment feed via RSS