主流免費 IP 歸屬地 API 對比
| 服務商 | 介面地址 | 回傳格式 | 頻率限制 | 準確度 | 適用場景 |
| 新浪 | https://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js&ip= | JavaScript 變數 | 無明確限制 | 國內較高 | 中小型專案、批次離線解析 |
| 淘寶 | https://ip.taobao.com/service/getIpInfo.php | JSON | 約 10 QPS/IP | 高(淘寶大數據) | 電商/高準確度需求 |
| 騰訊 | https://apis.map.qq.com/ws/location/v1/ip | JSON | 需 Key,免費額度 | 高(騰訊位置服務) | 正規商業專案、需 HTTPS |
| ip-api.com | https://ip-api.com/json/ | JSON | 45 req/min 免費 | 全球覆蓋 | 國際化業務 |
新浪 API 實作範例(同步 HTTP + 正則解析)
public class IpLocationUtil {
private static final String SINA_API = "https://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js&ip=";
public static String getLocationBySina(String ip) {
if (StringUtils.isBlank(ip)) return null;
try {
String url = SINA_API + ip;
String resp = HttpClientUtil.get(url); // 封裝 HttpClient/GetMethod
// 回傳範例:var remote_ip_info = {"ret":1,"start":"...","end":"...","country":"中國","province":"廣東","city":"廣州","district":"天河","isp":"電信","type":"","desc":""};
String ret = StringUtils.substringBetween(resp, "\"ret\":", ",");
if (!"1".equals(ret)) return null;
String province = decodeUnicode(StringUtils.substringBetween(resp, "\"province\":", ","));
String city = decodeUnicode(StringUtils.substringBetween(resp, "\"city\":", ","));
String district = decodeUnicode(StringUtils.substringBetween(resp, "\"district\":", ","));
String isp = decodeUnicode(StringUtils.substringBetween(resp, "\"isp\":", "}"));
return province + city + district + isp;
} catch (Exception e) {
log.error("IP查詢失敗: {}", ip, e);
return null;
}
}
// Unicode \uXXXX 解碼
private static String decodeUnicode(String str) { ... }
}
淘寶 API 實作(POST + JSON 解析)
public String getLocationByTaobao(String ip) {
String url = "https://ip.taobao.com/service/getIpInfo.php";
String param = "ip=" + ip;
String json = HttpClientUtil.post(url, param); // 回傳 JSON 字串
// {"code":0,"data":{"ip":"...","country":"中國","region":"廣東","city":"廣州","county":"天河","isp":"電信","area_id":"440100"}}
JSONObject obj = JSON.parseObject(json);
if (obj.getIntValue("code") != 0) return null;
JSONObject data = obj.getJSONObject("data");
return data.getString("country") + data.getString("region") + data.getString("city") + data.getString("county") + data.getString("isp");
}
高併發場景三大優化策略
1. 本地快取(Guava/Caffeine)
// 單機百萬級 IP 去重率極高,快取命中率 > 95%
private static final Cache<String, String> IP_CACHE = Caffeine.newBuilder()
.maximumSize(100_000)
.expireAfterWrite(30, TimeUnit.DAYS)
.build();
public String getLocationCached(String ip) {
return IP_CACHE.get(ip, k -> fetchFromApi(k)); // 原子獲取或計算
}
2. 批次異步預熱
- 離線任務:每日從日志提取去重 IP,多執行緒呼叫 API 落庫
ip_location 表。
- 線上服務:直接查本地資料庫/Redis,毫秒級回應,零外部依賴。
3. 連線池與超時控制
HttpClientBuilder.create()
.setMaxConnTotal(200)
.setMaxConnPerRoute(50)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(2000)
.setSocketTimeout(5000)
.build())
.build();
實測效能數據(單機 8 核 16G)
| 方案 | 10 萬 IP 耗時 | 成功率 | 備註 |
| 同步逐個呼叫淘寶 | ~2.5 小時 | 60%(頻繁 429/超時) | 不可用 |
| 同步逐個呼叫新浪 | ~10 分鐘 | 95% | 可接受 |
| 本地快取 + 新浪 | ~30 秒(含首次) | 99%+ | 生產推薦 |
| 離線批次入庫 + Redis 查詢 | 離線 5 分鐘 / 線上 1ms | 100% | 高併發標配 |
避坑指南
- 內網 IP(10.x, 172.16-31.x, 192.168.x)直接返回「內網」,不呼叫 API。
- IPv6:上述免費介面多不支援,需用 ip-api.com 或付費服務。
- 編碼陷阱:新浪回傳 JS Unicode,必須
decodeUnicode;淘寶回傳 UTF-8 JSON,直接解析。
- 法律合規:免費介面無 SLA,核心業務請採購商業授權(如 ipip.net、MaxMind GeoIP2 離線庫)。