在某些场景下,我们需要根据ip获得对应的城市名称。比如,我们需要统计访问ip在各城市的分布;如物联网环境中,设备接入按城市维度进行统计等。
到目前为止,很多开放接口,如淘宝、新浪、腾讯、百度等开放接口或失效或需要key。很多接口已不再免费调用,本文提供3种方式:
莫名API介绍
访问https://api.qzone.work/doc/ipaddress.html了解。其返回结果格式如下:
{
"msg":"success",
"code":10000,
"data":{
"country":"中国",
"province":"广东省",
"city":"深圳市",
"ip":"101.105.35.57",
"ISP":"鹏博士"
},
"time":0.00932,
"info":"欢迎使用莫名博客(Qzone.Work)旗下免费提供的API服务!"
}
代码步骤
1、构建HttpClient对象,调用莫名API的URL地址
String url = "https://api.qzone.work/api/ip.address?ip="+ip;
String cityName = "";
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(url);
2、解析返回的json字串即可
HttpResponse response = client.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
String strResult = EntityUtils.toString(response.getEntity());
JSONObject jsonResult = JSON.parseObject(strResult);
System.out.println(JSON.toJSONString(jsonResult, true));
JSONObject dataJson = jsonResult.getJSONObject("data");
cityName = dataJson.getString("city");
}
... ...
ip2region - 准确率99.9%的离线IP地址定位库,0.0x毫秒级查询,ip2region.db数据库只有数MB,提供了java,php,c,python,nodejs,golang,c#等查询绑定和Binary,B树,内存三种查询算法。
每条ip数据段都固定了格式:
_城市Id|国家|区域|省份|城市|ISP_
如;
{
"cityId":995,
"dataPtr":117853,
"region":"中国|华东|上海市|上海市|移动"
}
代码步骤:
1、准备db文件,如在src/main/resource目录下创建data目录,存放ip2region.db文件
2、引入ip2region依赖包
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>1.7.2</version>
</dependency>
3、读取ip2region.db文件,指定查询算法,如btreeSearch,然后获取DataBlock 结果,block的region就包含城市信息。
DbSearcher searcher = null;
try {
String dbPath = Ip2CityUtil.class.getClassLoader().getResource("data/ip2region.db").getPath();
File file = new File(dbPath);
if (file.exists()) {
DbConfig config = new DbConfig();
searcher = new DbSearcher(config, file.getPath());
Method method = searcher.getClass().getMethod("btreeSearch", String.class);
if (!Util.isIpAddress(ip)) {
System.out.println("Error: Invalid ip address");
}
DataBlock dataBlock = (DataBlock) method.invoke(searcher, ip);
System.out.println(JSON.toJSONString(dataBlock, true));
return dataBlock.getRegion();
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
GeoLite2和ip2region一样,先准备离线数据包、导入依赖包。
代码步骤:
1、准备db文件,如在src/main/resource目录下创建data目录,存放GeoLite2-City.mmdb文件
2、引入geolite2依赖包
<dependency>
<groupId>com.maxmind.geoip2</groupId>
<artifactId>geoip2</artifactId>
<version>2.14.0</version>
</dependency>
3、读取GeoLite2-City.mmdb文件,创建DatabaseReader对象 ,然后通过reader.city方法获取CityResponse结果,其中包含丰富的地理位置信息。
public static String getCityNameByGeoLite2(String ip) {
try {
InputStream database = Ip2CityUtil.class.getClassLoader().getResourceAsStream("data/GeoLite2-City.mmdb");
// 创建 DatabaseReader对象
DatabaseReader reader = new DatabaseReader.Builder(database).build();
InetAddress ipAddress = InetAddress.getByName(ip);
CityResponse response = reader.city(ipAddress);
System.out.println(JSON.toJSONString(response, true));
// System.out.println( response.getCity().getNames().get("zh-CN"));
return response.getCity().getNames().get("zh-CN");
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
4、一个CityResponse的内容示例如下:
{
"city":{
"geoNameId":1796236,
"name":"Shanghai",
"names":{
"de":"Shanghai",
"ru":"Шанхай",
"pt-BR":"Xangai",
"ja":"上海",
"en":"Shanghai",
"fr":"Shanghai",
"zh-CN":"上海",
"es":"Shanghai"
}
},
"continent":{
"code":"AS",
"geoNameId":6255147,
"name":"Asia",
"names":{
"de":"Asien",
"ru":"Азия",
"pt-BR":"Ásia",
"ja":"アジア",
"en":"Asia",
"fr":"Asie",
"zh-CN":"亚洲",
"es":"Asia"
}
},
"country":{
"geoNameId":1814991,
"inEuropeanUnion":false,
"isoCode":"CN",
"name":"China",
"names":{
"de":"China",
"ru":"Китай",
"pt-BR":"China",
"ja":"中国",
"en":"China",
"fr":"Chine",
"zh-CN":"中国",
"es":"China"
}
},
"leastSpecificSubdivision":{
"geoNameId":1796231,
"isoCode":"SH",
"name":"Shanghai",
"names":{
"en":"Shanghai",
"fr":"Municipalité de Shanghai",
"zh-CN":"上海",
"pt-BR":"Xangai"
}
},
"location":{
"accuracyRadius":20,
"latitude":31.0449,
"longitude":121.4012,
"timeZone":"Asia/Shanghai"
},
"maxMind":{
},
"mostSpecificSubdivision":{"$ref":"$.leastSpecificSubdivision"},
"postal":{
},
"registeredCountry":{
"geoNameId":1814991,
"inEuropeanUnion":false,
"isoCode":"CN",
"name":"China",
"names":{
"de":"China",
"ru":"Китай",
"pt-BR":"China",
"ja":"中国",
"en":"China",
"fr":"Chine",
"zh-CN":"中国",
"es":"China"
}
},
"representedCountry":{
"inEuropeanUnion":false,
"names":{}
},
"subdivisions":[
{"$ref":"$.leastSpecificSubdivision"}
],
"traits":{
"anonymous":false,
"anonymousProxy":false,
"anonymousVpn":false,
"hostingProvider":false,
"ipAddress":"183.194.238.19",
"legitimateProxy":false,
"network":{
"networkAddress":"183.194.128.0",
"prefixLength":17
},
"publicProxy":false,
"satelliteProvider":false,
"torExitNode":false
}
}
可以通过response.getCity().getNames().get("zh-CN")获取城市信息,如上述示例获取的是上海。
资源下载
https://pan.baidu.com/s/4pRI9z2F
GeoLite2的信息很全面,但相对而言其准确率并不是很精确。ip2region相对较准确点。