# 离线IP库
**Repository Path**: Super_TongYao/offline-ip-library
## Basic Information
- **Project Name**: 离线IP库
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-07-31
- **Last Updated**: 2025-07-31
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 描述
本文实现:
1、离线查询IP地址
2、IP地址精确到区域
3、IP地址支持国外IP
此时需要一个创建,比如我输入一个8.8.8.8的IP立马就需要返回给我一个中文地址信息,
类似于百度的IP搜索:
```
113.111.186.123
```

如果现在离线环境或者在线环境上本地部署一套IP地址查询,可以查阅本文章。
本文本地部署的结果演示(`注意!本文演示的项目不包含任何一个在线API接口!!!`):

首先感谢GItHub地址:https://github.com/ljxi/GeoCN
作者的在线演示地址:
```
https://ipv4.netart.cn/
https://ipv4.netart.cn/113.111.186.123
```
这个仓库是用的Python 的 Fast API 开发的,此时我需要一个Java的,本文会有Java的项目地址。
----------------------------------------------------------------------------------------
还有一种技术,只有springboot的,返回数据不是准确,感兴趣的可以自己去看看:
ip2region仓库地址:[https://gitee.com/lionsoul/ip2region](https://gitee.com/lionsoul/ip2region)
ip2region集成文章:[https://cloud.tencent.com/developer/article/2373096](https://cloud.tencent.com/developer/article/2373096)
----------------------------------------------------------------------------------------
## 开始
1、下载IP库
mmdb 仓库:[https://github.com/P3TERX/GeoLite.mmdb/releases](https://github.com/P3TERX/GeoLite.mmdb/releases)(不主要)
mmdb IP库下载:[https://github.com/P3TERX/GeoLite.mmdb/releases](https://github.com/P3TERX/GeoLite.mmdb/releases)
只需要下载以下两个mmdb文件
```
GeoLite2-City.mmdb
GeoLite2-ASN.mmdb
```
如果你的网络出不了国,可以下载国内的:[https://download.csdn.net/download/u014641168/91554209](https://download.csdn.net/download/u014641168/91554209)
还需下载:
```
GeoCN.mmdb
```
这个文件在作者的仓库中就能找到,地址:[https://github.com/ljxi/GeoCN/releases/tag/Latest](https://github.com/ljxi/GeoCN/releases/tag/Latest)
如果打不开,看国内:[https://download.csdn.net/download/u014641168/91554230](https://download.csdn.net/download/u014641168/91554230) (我忘了整合到一块,将就着看吧)
## 代码
### python
首先作者的代码时Python的,Python版本是3.9的,需要手动安装插件库
```
pip install ipaddress
pip install maxminddb
```
还有fastapi
1、代码如下:
```
import ipaddress
import maxminddb
from fastapi import FastAPI, Request
city_reader = maxminddb.open_database('C:\\Users\\ad\\Desktop\\ip\\GeoLite2-City.mmdb')
asn_reader = maxminddb.open_database('C:\\Users\\ad\\Desktop\\ip\\GeoLite2-ASN.mmdb')
cn_reader = maxminddb.open_database('C:\\Users\\ad\\Desktop\\ip\\GeoCN.mmdb')
lang = ["zh-CN","en"]
asn_map = {
9812:"东方有线",
9389:"中国长城",
17962:"天威视讯",
17429:"歌华有线",
7497:"科技网",
24139:"华数",
9801:"中关村",
4538:"教育网",
24151:"CNNIC",
38019:"中国移动",139080:"中国移动",9808:"中国移动",24400:"中国移动",134810:"中国移动",24547:"中国移动",
56040:"中国移动",56041:"中国移动",56042:"中国移动",56044:"中国移动",132525:"中国移动",56046:"中国移动",
56047:"中国移动",56048:"中国移动",59257:"中国移动",24444:"中国移动",
24445:"中国移动",137872:"中国移动",9231:"中国移动",58453:"中国移动",
4134:"中国电信",4812:"中国电信",23724:"中国电信",136188:"中国电信",137693:"中国电信",17638:"中国电信",
140553:"中国电信",4847:"中国电信",140061:"中国电信",136195:"中国电信",17799:"中国电信",139018:"中国电信",
133776:"中国电信",58772:"中国电信",146966:"中国电信",63527:"中国电信",58539:"中国电信",58540:"中国电信",
141998:"中国电信",138169:"中国电信",139203:"中国电信",58563:"中国电信",137690:"中国电信",63838:"中国电信",
137694:"中国电信",137698:"中国电信",136167:"中国电信",148969:"中国电信",134764:"中国电信",
134770:"中国电信",148981:"中国电信",134774:"中国电信",136190:"中国电信",140647:"中国电信",
132225:"中国电信",140485:"中国电信",4811:"中国电信",131285:"中国电信",137689:"中国电信",
137692:"中国电信",140636:"中国电信",140638:"中国电信",140345:"中国电信",38283:"中国电信",
140292:"中国电信",140903:"中国电信",17897:"中国电信",134762:"中国电信",139019:"中国电信",
141739:"中国电信",141771:"中国电信",134419:"中国电信",140276:"中国电信",58542:"中国电信",
140278:"中国电信",139767:"中国电信",137688:"中国电信",137691:"中国电信",4809:"中国电信",
58466:"中国电信",137687:"中国电信",134756:"中国电信",134760:"中国电信",
133774:"中国电信",133775:"中国电信",4816:"中国电信",134768:"中国电信",
58461:"中国电信",58519:"中国电信",58520:"中国电信",131325:"中国电信",
4837:"中国联通",4808:"中国联通",134542:"中国联通",134543:"中国联通",10099:"中国联通",
140979:"中国联通",138421:"中国联通",17621:"中国联通",17622:"中国联通",17816:"中国联通",
140726:"中国联通",17623:"中国联通",136958:"中国联通",9929:"中国联通",58519:"中国联通",
140716:"中国联通",4847:"中国联通",136959:"中国联通",135061:"中国联通",139007:"中国联通",
59019:"金山云",
135377:"优刻云",
45062:"网易云",
137718:"火山引擎",
37963:"阿里云",45102:"阿里云国际",
45090:"腾讯云",132203:"腾讯云国际",
55967:"百度云",38365:"百度云",
58519:"华为云", 55990:"华为云",136907:"华为云",
4609:"澳門電訊",
134773:"珠江宽频",
1659:"台湾教育网",
8075:"微软云",
17421:"中华电信",
3462:"HiNet",
13335:"Cloudflare",
55960:"亚马逊云",14618:"亚马逊云",16509:"亚马逊云",
15169:"谷歌云",396982:"谷歌云",36492:"谷歌云",
}
def get_as_info(number):
r = asn_map.get(number)
if r:
return r
def get_des(d):
for i in lang:
if i in d['names']:
return d['names'][i]
return d['names']['en']
def get_country(d):
r = get_des(d)
if r in ["香港", "澳门", "台湾"]:
return "中国" + r
return r
def province_match(s):
arr=['内蒙古','黑龙江','河北','山西','吉林','辽宁','江苏','浙江','安徽','福建','江西','山东','河南','湖北','湖南','广东','海南','四川','贵州','云南','陕西','甘肃','青海','广西','西藏','宁夏','新疆','北京','天津','上海','重庆']
for i in arr:
if i in s:
return i
return ''
def de_duplicate(regions):
regions = filter(bool,regions)
ret = []
[ret.append(i) for i in regions if i not in ret]
return ret
def get_addr(ip, mask):
network = ipaddress.ip_network(f"{ip}/{mask}", strict=False)
first_ip = network.network_address
return f"{first_ip}/{mask}"
def get_maxmind(ip: str):
ret = {"ip":ip}
asn_info = asn_reader.get(ip)
if asn_info:
as_ = {"number":asn_info["autonomous_system_number"],"name":asn_info["autonomous_system_organization"]}
info = get_as_info(as_["number"])
if info:
as_["info"] = info
ret["as"] = as_
city_info, prefix = city_reader.get_with_prefix_len(ip)
ret["addr"] = get_addr(ip, prefix)
if not city_info:
return ret
if "country" in city_info:
country_code = city_info["country"]["iso_code"]
country_name = get_country(city_info["country"])
ret["country"] = {"code":country_code,"name":country_name}
if "registered_country" in city_info:
registered_country_code = city_info["registered_country"]["iso_code"]
ret["registered_country"] = {"code":registered_country_code,"name":get_country(city_info["registered_country"])}
regions = [get_des(i) for i in city_info.get('subdivisions', [])]
if "city" in city_info:
c = get_des(city_info["city"])
if (not regions or c not in regions[-1])and c not in country_name:
regions.append(c)
regions = de_duplicate(regions)
if regions:
ret["regions"] = regions
return ret
def get_cn(ip:str, info={}):
ret, prefix = cn_reader.get_with_prefix_len(ip)
if not ret:
return
info["addr"] = get_addr(ip, prefix)
regions = de_duplicate([ret["province"],ret["city"],ret["districts"]])
if regions:
info["regions"] = regions
info["regions_short"] = de_duplicate([province_match(ret["province"]),ret["city"].replace('市',''),ret["districts"]])
if "as" not in info:
info["as"] = {}
info["as"]["info"] = ret['isp']
if ret['net']:
info["type"] = ret['net']
return ret
def get_ip_info(ip):
info = get_maxmind(ip)
if "country" in info and info["country"]["code"] == "CN" and ("registered_country" not in info or info["registered_country"]["code"] == "CN"):
get_cn(ip,info)
return info
def query():
while True:
try:
ip = input('IP: \t').strip()
info = get_ip_info(ip)
print(f"网段:\t{info['addr']}")
if "as" in info:
print(f"ISP:\t",end=' ')
if "info" in info["as"]:
print(info["as"]["info"],end=' ')
else:
print(info["as"]["name"],end=' ')
if "type" in info:
print(f"({info['type']})",end=' ')
print(f"ASN{info['as']['number']}",end=' ')
print(info['as']["name"])
if "registered_country" in info and ("country" not in info or info["country"]["code"] != info["registered_country"]["code"]):
print(f"注册地:\t{info['registered_country']['name']}")
if "country" in info:
print(f"使用地:\t{info['country']['name']}")
if "regions" in info:
print(f"位置: \t{' '.join(info['regions'])}")
except Exception as e:
print(e)
raise e
finally:
print("\n")
app = FastAPI()
@app.get("/")
def api(request: Request, ip: str = None):
if not ip:
xff = request.headers.get("x-forwarded-for")
if xff:
ip = xff.split(",")[0]
else:
ip = request.headers.get("x-real-ip") or request.client.host
return get_ip_info(ip.strip())
@app.get("/{ip}")
def path_api(ip):
return get_ip_info(ip)
if __name__ == '__main__':
query()
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8080, server_header=False, proxy_headers=True)
```
2、启动
```
uvicorn main:app --port 9000 --reload
```
3、浏览器访问
```
http://localhost:9000/113.111.186.123
```
### Java
作者的Python代码不满足我的需求,手动转成Java格式
我的jdk1.8,maven 3.6
1、引入依赖
```
com.maxmind.db
maxmind-db
2.1.0
```
2、代码类:
ASInfo.java
```
import lombok.Data;
/**
* @author: tongyao
* @since: 2025-07-31 09:32
*/
@Data
public class ASInfo {
private Integer number;
private String name;
private String info;
}
```
CountryInfo.java
```
import lombok.Data;
/**
* @author: tongyao
* @since: 2025-07-31 09:32
*/
@Data
public class CountryInfo {
private String code;
private String name;
}
```
GeoCNResponse.java
```
import lombok.Data;
@Data
public class GeoCNResponse {
private String province;
private String city;
private String districts;
private String isp;
private String net;
private int prefixLen;
// 移除MaxMindDbConstructor注解
public GeoCNResponse(
String province,
String city,
String districts,
String isp,
String net,
int prefixLen
) {
this.province = province;
this.city = city;
this.districts = districts;
this.isp = isp;
this.net = net;
this.prefixLen = prefixLen;
}
}
```
IPInfo.java
```
import lombok.Data;
import java.util.List;
@Data
public class IPInfo {
private String ip;
private String addr;
private String error;
private String type;
private CountryInfo country;
private CountryInfo registeredCountry;
private List regions;
private List regionsShort;
private ASInfo as;
}
```
GeoIPConfig.java
```
import com.maxmind.db.Reader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
import java.io.IOException;
@Configuration
public class GeoIPConfig {
@Bean
public Reader cityReader() throws IOException {
return new Reader(new File("C:\\Users\\ad\\Desktop\\ip\\GeoLite2-City.mmdb"));
}
@Bean
public Reader asnReader() throws IOException {
return new Reader(new File("C:\\Users\\ad\\Desktop\\ip\\GeoLite2-ASN.mmdb"));
}
@Bean
public Reader cnReader() throws IOException {
return new Reader(new File("C:\\Users\\ad\\Desktop\\ip\\GeoCN.mmdb"));
}
}
```
GeoIPService.java
```
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public interface GeoIPService {
IPInfo getIpInfo(String ip);
IPInfo getIpInfo(HttpServletRequest request);
}
```
GeoIPServiceImpl.java
```
import com.maxmind.db.Reader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.InetAddress;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class GeoIPServiceImpl implements GeoIPService {
private final Reader cityReader;
private final Reader asnReader;
private final Reader cnReader;
private final List lang = Arrays.asList("zh-CN", "en");
private final List provinces = Arrays.asList(
"内蒙古", "黑龙江", "河北", "山西", "吉林", "辽宁", "江苏", "浙江",
"安徽", "福建", "江西", "山东", "河南", "湖北", "湖南", "广东",
"海南", "四川", "贵州", "云南", "陕西", "甘肃", "青海", "广西",
"西藏", "宁夏", "新疆", "北京", "天津", "上海", "重庆"
);
private static final Map ASN_MAP = new HashMap() {{
// 有线电视/网络服务商
put(9812, "东方有线");
put(9389, "中国长城");
put(17962, "天威视讯");
put(17429, "歌华有线");
put(7497, "科技网");
put(24139, "华数");
put(9801, "中关村");
put(4538, "教育网");
put(24151, "CNNIC");
// 中国移动
put(38019, "中国移动");
put(139080, "中国移动");
put(9808, "中国移动");
put(24400, "中国移动");
put(134810, "中国移动");
put(24547, "中国移动");
put(56040, "中国移动");
put(56041, "中国移动");
put(56042, "中国移动");
put(56044, "中国移动");
put(132525, "中国移动");
put(56046, "中国移动");
put(56047, "中国移动");
put(56048, "中国移动");
put(59257, "中国移动");
put(24444, "中国移动");
put(24445, "中国移动");
put(137872, "中国移动");
put(9231, "中国移动");
put(58453, "中国移动");
// 中国电信
put(4134, "中国电信");
put(4812, "中国电信");
put(23724, "中国电信");
put(136188, "中国电信");
put(137693, "中国电信");
put(17638, "中国电信");
put(140553, "中国电信");
put(4847, "中国电信");
put(140061, "中国电信");
put(136195, "中国电信");
put(17799, "中国电信");
put(139018, "中国电信");
put(133776, "中国电信");
put(58772, "中国电信");
put(146966, "中国电信");
put(63527, "中国电信");
put(58539, "中国电信");
put(58540, "中国电信");
put(141998, "中国电信");
put(138169, "中国电信");
put(139203, "中国电信");
put(58563, "中国电信");
put(137690, "中国电信");
put(63838, "中国电信");
put(137694, "中国电信");
put(137698, "中国电信");
put(136167, "中国电信");
put(148969, "中国电信");
put(134764, "中国电信");
put(134770, "中国电信");
put(148981, "中国电信");
put(134774, "中国电信");
put(136190, "中国电信");
put(140647, "中国电信");
put(132225, "中国电信");
put(140485, "中国电信");
put(4811, "中国电信");
put(131285, "中国电信");
put(137689, "中国电信");
put(137692, "中国电信");
put(140636, "中国电信");
put(140638, "中国电信");
put(140345, "中国电信");
put(38283, "中国电信");
put(140292, "中国电信");
put(140903, "中国电信");
put(17897, "中国电信");
put(134762, "中国电信");
put(139019, "中国电信");
put(141739, "中国电信");
put(141771, "中国电信");
put(134419, "中国电信");
put(140276, "中国电信");
put(58542, "中国电信");
put(140278, "中国电信");
put(139767, "中国电信");
put(137688, "中国电信");
put(137691, "中国电信");
put(4809, "中国电信");
put(58466, "中国电信");
put(137687, "中国电信");
put(134756, "中国电信");
put(134760, "中国电信");
put(133774, "中国电信");
put(133775, "中国电信");
put(4816, "中国电信");
put(134768, "中国电信");
put(58461, "中国电信");
put(58519, "中国电信");
put(58520, "中国电信");
put(131325, "中国电信");
// 中国联通
put(4837, "中国联通");
put(4808, "中国联通");
put(134542, "中国联通");
put(134543, "中国联通");
put(10099, "中国联通");
put(140979, "中国联通");
put(138421, "中国联通");
put(17621, "中国联通");
put(17622, "中国联通");
put(17816, "中国联通");
put(140726, "中国联通");
put(17623, "中国联通");
put(136958, "中国联通");
put(9929, "中国联通");
put(58519, "中国联通");
put(140716, "中国联通");
put(4847, "中国联通");
put(136959, "中国联通");
put(135061, "中国联通");
put(139007, "中国联通");
// 云服务商
put(59019, "金山云");
put(135377, "优刻云");
put(45062, "网易云");
put(137718, "火山引擎");
put(37963, "阿里云");
put(45102, "阿里云国际");
put(45090, "腾讯云");
put(132203, "腾讯云国际");
put(55967, "百度云");
put(38365, "百度云");
put(58519, "华为云");
put(55990, "华为云");
put(136907, "华为云");
// 其他运营商
put(4609, "澳門電訊");
put(134773, "珠江宽频");
put(1659, "台湾教育网");
put(8075, "微软云");
put(17421, "中华电信");
put(3462, "HiNet");
put(13335, "Cloudflare");
put(55960, "亚马逊云");
put(14618, "亚马逊云");
put(16509, "亚马逊云");
put(15169, "谷歌云");
put(396982, "谷歌云");
put(36492, "谷歌云");
}};
@Autowired
public GeoIPServiceImpl(Reader cityReader, Reader asnReader, Reader cnReader) {
this.cityReader = cityReader;
this.asnReader = asnReader;
this.cnReader = cnReader;
}
@Override
public IPInfo getIpInfo(String ip) {
IPInfo info = new IPInfo();
info.setIp(ip);
try {
InetAddress ipAddress = InetAddress.getByName(ip);
// 处理ASN信息
processAsnInfo(ipAddress, info);
// 处理City信息
Map cityInfo = cityReader.get(ipAddress,Map.class);
if (cityInfo != null) {
processCityInfo(cityInfo, info);
// 如果是中国IP,处理GeoCN数据库
if (isChinaIp(info)) {
processCnInfo(ipAddress, info);
}
}
} catch (Exception e) {
e.printStackTrace();
info.setError(e.getMessage());
}
return info;
}
@Override
public IPInfo getIpInfo(HttpServletRequest request) {
String ip = Optional.ofNullable(request.getHeader("X-Forwarded-For"))
.map(xff -> xff.split(",")[0])
.orElse(request.getRemoteAddr());
return getIpInfo(ip);
}
private void processAsnInfo(InetAddress ipAddress, IPInfo info) throws IOException {
Map asnInfo = asnReader.get(ipAddress,Map.class);
if (asnInfo == null) return;
ASInfo as = new ASInfo();
as.setNumber(Integer.parseInt(asnInfo.get("autonomous_system_number").toString()));
as.setName(asnInfo.get("autonomous_system_organization").toString());
String ispName = getAsInfo(as.getNumber());
if (ispName != null) {
as.setInfo(ispName);
}
info.setAs(as);
}
private void processCityInfo(Map cityInfo, IPInfo info) {
// 处理网络前缀
Integer prefix = (Integer) cityInfo.get("prefix_len");
if (prefix == null) prefix = 24; // 默认值
info.setAddr(getAddr(info.getIp(), prefix));
// 处理国家信息
if (cityInfo.get("country") != null) {
Map country = (Map) cityInfo.get("country");
CountryInfo countryInfo = new CountryInfo();
countryInfo.setCode((String) country.get("iso_code"));
countryInfo.setName(getCountryName(country));
info.setCountry(countryInfo);
}
// 处理注册国家信息
if (cityInfo.get("registered_country") != null) {
Map regCountry = (Map) cityInfo.get("registered_country");
CountryInfo regCountryInfo = new CountryInfo();
regCountryInfo.setCode((String) regCountry.get("iso_code"));
regCountryInfo.setName(getCountryName(regCountry));
info.setRegisteredCountry(regCountryInfo);
}
// 处理地区信息
List regions = new ArrayList<>();
if (cityInfo.get("subdivisions") != null) {
List