根据IP地址获取所在城市的天气情况(Java)

主要思路:

1.通过request请求获取到浏览者的IP地址
2.通过这个IP地址调用外部接口获取到该IP地址所在的城市名称
3.获取到城市名字后,再次以该城市名字作为参数调用外部接口(天气),从而获取到该城市的天气信息。

调用外部接口:聚合数据
当然,我们自己怎么可能知道IP地址所在的城市和天气呢,还是要靠接口啊。
(在巨人的肩膀上编程的我觉得~真香)
聚合数据-网址 https://www.juhe.cn/
当然,这个接口网站提供了很多其他实用而且免费的接口,使用它的原因也是因为相对于其他几个接口网站使用起来更上手。

1.常规操作,来这里注册登录
在这里插入图片描述
2.注册后登录并添加接口
(可能也会有一系列的邮箱认证或者其他身份确认,这里就不再赘述啦)
直接点击 API,筛选类型【免费】 如下图:
在这里插入图片描述
我们要实现的功能就是【IP地址】【天气预报】这两个接口啦。
分别点击他们,在之后跳转的页面–立即申请 --即可。
申请完之后点击右上方–个人中心– —>点击–我的接口– 。如图:

在这里插入图片描述
在–我的接口–里面可以看到,你申请过的接口都有哪些,下图包含了一些我其他的接口,天气预那个 在下面,我就不截图了。
在这里插入图片描述
3测试一下接口
点击接口后面的测试,我们可以去亲自测试一下效果
在这里插入图片描述
223.104.212.171 这个IP地址为例子,这个是上海的一个IP地址
全部都以默认为主即可,直接复制粘贴223.104.212.171,发送请求
在这里插入图片描述
结果返回如下图:
在这里插入图片描述
看到返回的json中resultcode:200 就说明请求成功了,成功通过IP地址获取到了所在城市名称。
按照我们的思路接下来,根据上海这个城市名称获取到他的天气信息
和IP一样,测试天气预报这个接口,如图:
在这里插入图片描述
填上上海请求后,你可以掏出手机比对一下,看看天气信息对不对。
在这里插入图片描述
上图会预报接下来好几天的天气,太长了,我就不全部截图下来了。

以上我们的接口测试基本完成,接下来开始使用Java实现
首先解决第一个问题,我们通过request请求获取到的IP地址是不够严谨的,也许该IP地址经历了重重的反向代理早已经不是浏览页面的浏览者本人所在城市的IP地址了。所以通过一段代码先对IP进行解析,以方便获取他的真实IP地址。
新建一个Class命名IPUtil(你也可以自己命名)

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class IpUtil {
   public static String getIpAddr(HttpServletRequest request) {
       String ipAddress = null;
       try {
           ipAddress = request.getHeader("x-forwarded-for");
           if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
               ipAddress = request.getHeader("Proxy-Client-IP");
           }
           if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
               ipAddress = request.getHeader("WL-Proxy-Client-IP");
           }
           if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
               ipAddress = request.getRemoteAddr();
               if (ipAddress.equals("127.0.0.1")) {
                   // 根据网卡取本机配置的IP
                   InetAddress inet = null;
                   try {
                       inet = InetAddress.getLocalHost();
                   } catch (UnknownHostException e) {
                       e.printStackTrace();
                   }
                   ipAddress = inet.getHostAddress();
               }
           }
           // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
           if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
               // = 15
               if (ipAddress.indexOf(",") > 0) {
                   ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
               }
           }
       } catch (Exception e) {
           ipAddress="";
       }
       // ipAddress = this.getRequest().getRemoteAddr();

       return ipAddress;
   }
}

我们看到,该类将HttpServletRequest request这个请求转化为了一个ipAddress返还出来。我们就以这个ipAddress来继续调用天气预报的接口。
顺便一提~~之后所有外部访问的请求我们都会使用该工具类对外部request请求进行一次抓取真实IP地址的操作,以保证我们的访问者看到的确实是他所在城市的天气,而不会出现身在北方确看到南方天气的乌龙情况。

我们保留上面获取真实IP这个问题,先来测试好我们的接口。
新建一个APIUtil类,用来对我们的接口代码进行测试(部分调用的工具类代码附在文章末尾)
APIUtil中主要包含getCityByIP() getWeatherByCity()这两个方法,作为我们调用的主要方法。

	public static void main(String[] args) {
		String ipAddress = "223.104.212.171";
		String city = getCityByIP(ipAddress);
		
		System.out.println("获取到城市名称:"+city);
	}

	//根据IP获取所在城市的名称
	private static String getCityByIP(String IP) {
		String city ="";
		String jsonStr = "";
		String strUrl = "http://apis.juhe.cn/ip/ipNew";
		String srtVlaue = "ip="+IP+"&key=022287affefd4e498740d2d107efbe4c";
		jsonStr = HttpGetPost.sendGet(strUrl,srtVlaue);
		System.out.println(jsonStr);
		if(jsonStr.equals("请求超时")) {
			return "获取城市名称失败";
		}
		JSONObject json = JSONObject.fromObject(jsonStr);
		String resultcode = (String) json.get("resultcode");
		if(resultcode.equals("200")) {//接口請求成功
			JSONObject result = (JSONObject) json.get("result");
			city = (String) result.get("City");
		}else {
			city = "所在城市获取失败";
		}
		if(city.contains("市")) {
			city = city.substring(0, 2);//只截取前两位汉字
		}
		System.out.println("根据IP:"+IP+"获取城市名称为:"+city);
		return city;
	}
	
	//根据城市名称获取该城市的天气状况
	private static Map<String,Object> getWeatherByCity(String City) {
		Map<String,Object> hashmap = new HashMap<String, Object>();
		String jsonStr = "";
		String strUrl = "http://apis.juhe.cn/simpleWeather/query";
		String srtVlaue = "city="+City+"&key=93e7573d94a0b85b2ac38ded5aef41e0";
		jsonStr = HttpGetPost.sendGet(strUrl,srtVlaue);
		System.out.println(jsonStr);
		JSONObject json = JSONObject.fromObject(jsonStr);
		String reason = (String) json.get("reason");
		if(reason.equals("查询成功!")) {
			hashmap = JsonUtil.parseJSONstr2Map(jsonStr);
			
		}else {
			hashmap.put("error_code", "获取"+City+"天气失败");
		}
		
		return hashmap;
	}

先假设这个访问的IP地址是223.104.212.171,我们先用getCityByIP()方法获取到地址223.104.212.171对应的城市“上海”。之后再调用getWeatherByCity()方法获取到城市"上海"的天气信息并显示在Console控制台中,方便我们查看是否获取成功。

运行结果如图:

在这里插入图片描述
我们从Console中可以看到,223.104.212.171这个IP地址的对应城市是上海。API接口返回了一系列json字符串:{“resultcode”:“200”,“reason”:“查询成功”,“result”:{“Country”:“中国”,“Province”:“上海”,“City”:“上海市”,“Isp”:“移动”},“error_code”:0}

接下来,我们将抓到的City程式再作为参数调用getWeatherByCity(city)这个方法,用来获取“上海”的天气信息。
改变main方法的代码如下:

public static void main(String[] args) {
		
		String ipAddress = "223.104.212.171";
		String city = getCityByIP(ipAddress);
//		System.out.println("获取到城市名称:"+city);
		Map<String,Object> weather = getWeatherByCity(city);
		System.out.println(weather.toString());
		
	}

运行结果如图:
在这里插入图片描述
通过"上海"(city参数),我们又调用API接口获取到了该城市的天气。
而第三方API给我们提供的实际就是这样一个接口调用:
http://apis.juhe.cn/simpleWeather/query?city=%E4%B8%8A%E6%B5%B7&key=93e7573d94a0b85b2ac38ded5aef41e0
其中包含了当天天气和接下来几天的天气,从json中我们也可以看到。

{"reason":"查询成功!","result":{"city":"上海","realtime":{"temperature":"16","humidity":"33","info":"多云","wid":"01","direct":"西风","power":"2级","aqi":"31"},"future":[{"date":"2020-03-16","temperature":"9\/16℃","weather":"多云转阴","wid":{"day":"01","night":"02"},"direct":"东南风"},{"date":"2020-03-17","temperature":"11\/18℃","weather":"阴","wid":{"day":"02","night":"02"},"direct":"西南风"},{"date":"2020-03-18","temperature":"12\/21℃","weather":"多云转晴","wid":{"day":"01","night":"00"},"direct":"西南风转南风"},{"date":"2020-03-19","temperature":"10\/21℃","weather":"多云","wid":{"day":"01","night":"01"},"direct":"北风转东南风"},{"date":"2020-03-20","temperature":"13\/20℃","weather":"晴","wid":{"day":"00","night":"00"},"direct":"南风"}]},"error_code":0}

之后,你可以把获取到的参数:Map<String,Object> weather 封装为一个你想要的bean,用于在前端页面显示使用。
例如这样:

package com.wangyang.web.vo;

public class Weather {
	private String City;
	private String Aqi;
	private String Direct;
	private String Futureweather;
	private String Humidity;
	private String Info;
	private String Power;
	private String Temperature;
	private String WeatherImgUrl;
	
	
	
	public Weather() {
		super();
	}
	
	public Weather(String city, String aqi, String direct, String futureweather, String humidity, String info,
			String power, String temperature, String weatherImgUrl) {
		super();
		City = city;
		Aqi = aqi;
		Direct = direct;
		Futureweather = futureweather;
		Humidity = humidity;
		Info = info;
		Power = power;
		Temperature = temperature;
		WeatherImgUrl = weatherImgUrl;
	}

	public String getCity() {
		return City;
	}
	public void setCity(String city) {
		City = city;
	}
	public String getAqi() {
		return Aqi;
	}
	public void setAqi(String aqi) {
		Aqi = aqi;
	}
	public String getDirect() {
		return Direct;
	}
	public void setDirect(String direct) {
		Direct = direct;
	}
	public String getFutureweather() {
		return Futureweather;
	}
	public void setFutureweather(String futureweather) {
		Futureweather = futureweather;
	}
	public String getHumidity() {
		return Humidity;
	}
	public void setHumidity(String humidity) {
		Humidity = humidity;
	}
	public String getInfo() {
		return Info;
	}
	public void setInfo(String info) {
		Info = info;
	}
	public String getPower() {
		return Power;
	}
	public void setPower(String power) {
		Power = power;
	}
	public String getTemperature() {
		return Temperature;
	}
	public void setTemperature(String temperature) {
		Temperature = temperature;
	}
	public String getWeatherImgUrl() {
		return WeatherImgUrl;
	}
	public void setWeatherImgUrl(String weatherImgUrl) {
		WeatherImgUrl = weatherImgUrl;
	}

	@Override
	public String toString() {
		return "Weather [City=" + City + ", Aqi=" + Aqi + ", Direct=" + Direct + ", Futureweather=" + Futureweather
				+ ", Humidity=" + Humidity + ", Info=" + Info + ", Power=" + Power + ", Temperature=" + Temperature
				+ ", WeatherImgUrl=" + WeatherImgUrl + "]";
	}
	
	

}

回到刚开始提到的获取真实IP的问题,在其他博文中引用了这个方法:
附上链接:https://blog.csdn.net/yulei_qq/article/details/49152613

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class IpUtil {
   public static String getIpAddr(HttpServletRequest request) {
       String ipAddress = null;
       try {
           ipAddress = request.getHeader("x-forwarded-for");
           if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
               ipAddress = request.getHeader("Proxy-Client-IP");
           }
           if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
               ipAddress = request.getHeader("WL-Proxy-Client-IP");
           }
           if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
               ipAddress = request.getRemoteAddr();
               if (ipAddress.equals("127.0.0.1")) {
                   // 根据网卡取本机配置的IP
                   InetAddress inet = null;
                   try {
                       inet = InetAddress.getLocalHost();
                   } catch (UnknownHostException e) {
                       e.printStackTrace();
                   }
                   ipAddress = inet.getHostAddress();
               }
           }
           // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
           if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
               // = 15
               if (ipAddress.indexOf(",") > 0) {
                   ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
               }
           }
       } catch (Exception e) {
           ipAddress="";
       }
       // ipAddress = this.getRequest().getRemoteAddr();

       return ipAddress;
   }
}

在实际开发中,使用该方法过滤request请求即可获取真实IP地址(适用于大多数情况)。
在这里插入图片描述
到此,基本的方法调用和使用就介绍OK了。
附上我的个人博客首页(Weikisa’s Blog)其中就包含了该天气调用的展示,如果有兴趣可以尝试一下。虽然是个简单的API调用,但是在实际开发中也会遇到不少问题,一步步纠错学习,相信也一定会给你带来不少收获。

结尾附上上文中会使用到的工具类:
1.HttpGetPost:常见的接口调用工具,基于http超文本传输协议。 你也可以自行使用HttpClient或者你熟悉的方式。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;


public class HttpGetPost {
    /**
     * 向指定URL发送GET方法的请求
     * 
     * @param url
     *            发送请求的URL
     * @param param
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return URL 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            connection.setConnectTimeout(3000);
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            connection.connect();
            // 获取所有响应头字段
            Map<String, List<String>> map = connection.getHeaderFields();
            // 遍历所有的响应头字段
            for (String key : map.keySet()) {
                System.out.println(key + "--->" + map.get(key));
            }
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送GET请求出现异常!" + e);
            e.printStackTrace();
            return "请求超时";
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 向指定 URL 发送POST方法的请求
     * 
     * @param url
     *            发送请求的 URL
     * @param param
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            conn.setConnectTimeout(10000);
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常!"+e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }    
}

2.JsonUtil:包含了将json转为Map和HashMap数据结构的方法,便于获取json中的key-value值。

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
 
import java.util.*;
 
public class JsonUtil {
    
    /**
     * 将json对象转换为HashMap
     * @param json
     * @return
     */
    public static Map<String, Object> parseJSON2Map(JSONObject json) {
        Map<String, Object> map = new HashMap<String, Object>();
        // 最外层解析
        for (Object k : json.keySet()) {
            Object v = json.get(k);
            // 如果内层还是json数组的话,继续解析
            if (v instanceof JSONArray) {
                List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
                Iterator<JSONObject> it = ((JSONArray) v).iterator();
                while (it.hasNext()) {
                    JSONObject json2 = it.next();
                    list.add(parseJSON2Map(json2));
                }
                map.put(k.toString(), list);
            } else if (v instanceof JSONObject) {
                // 如果内层是json对象的话,继续解析
                map.put(k.toString(), parseJSON2Map((JSONObject) v));
            } else {
		// 如果内层是普通对象的话,直接放入map中
                map.put(k.toString(), v);
            }
        }
        return map;
    }
 
    /**将json字符串转换为Map
     * @param jsonStr
     * @return
     */
    public static Map<String, Object> parseJSONstr2Map(String jsonStr) {
        JSONObject json = JSONObject.fromObject(jsonStr);
        Map<String, Object> map = parseJSON2Map(json);
        return map;
    }
}

3.使用到API接口调用,必不可少的的当然是json字符串的应用。这个需要引用外部jar包,引用阿里的FastJson或者你熟悉的常用的json-lib都可以。
或者看下这篇文章对json的一个简单应用。
https://blog.csdn.net/qq_41334351/article/details/95956065

结尾附上我的博客地址,功能简陋,域名申请也迟迟没有过审,不要介意,哈哈~
http://114.55.95.46

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值