網站後台需解析用戶 IP 歸屬地理位置(國家、省份、城市),用於訪問統計、風控攔截、內容分發等場景。本文對比三大主流免費 API 並提供生產級封裝代碼。
| 服務商 | 接口地址 | 返回格式 | 頻率限制 | 特點 |
|---|---|---|---|---|
| 騰訊 IP 分享計劃 | https://ip.qq.com/cgi-bin/searchip?searchip1= | HTML 文本 | 無官方說明 | 中文友好、需解析 HTML |
| 新浪 IP 查詢 | https://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip= | JSON | 較寬鬆 | 結構化、支持 JSONP |
| 淘寶 IP 庫 | https://ip.taobao.com/service/getIpInfo.php?ip= | JSON | QPS 限制 | 數據準確、官方維護 |
public class IpLocationService {
private static final String QQ_IP_API = "https://ip.qq.com/cgi-bin/searchip?searchip1=";
public String getAddressByIP(String ip) {
if (ip == null || ip.trim().isEmpty()) {
return "無效 IP";
}
try {
URL url = new URL(QQ_IP_API + URLEncoder.encode(ip, "UTF-8"));
URLConnection conn = url.openConnection();
conn.setConnectTimeout(3000);
conn.setReadTimeout(5000);
// 響應編碼為 GBK
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "GBK"))) {
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line);
}
return parseQQResponse(result.toString());
}
} catch (IOException e) {
return "查詢失敗: " + e.getMessage();
}
}
private String parseQQResponse(String html) {
// 響應包含: "該IP地址為:廣東省廣州市 騰訊計算機系統有限公司"
String prefix = "該IP地址為:";
int start = html.indexOf(prefix);
if (start == -1) return "解析失敗";
start += prefix.length();
int end = html.indexOf(" ", start);
if (end == -1) end = html.length();
String location = html.substring(start, end).trim();
// 進一步拆分省市
String province = "";
String city = "";
int provinceEnd = location.indexOf("省");
if (provinceEnd != -1) {
province = location.substring(0, provinceEnd + 1);
int cityEnd = location.indexOf("市", provinceEnd);
if (cityEnd != -1) {
city = location.substring(provinceEnd + 1, cityEnd + 1);
}
}
return String.format("省份: %s, 城市: %s, 完整: %s", province, city, location);
}
}
public class SinaIpLocationService {
private static final String SINA_API = "https://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=";
public IpLocation getLocation(String ip) {
String urlStr = SINA_API + ip;
try (InputStream is = new URL(urlStr).openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
StringBuilder json = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) json.append(line);
// 簡單 JSON 解析(生產環境建議用 Jackson/Gson)
String jsonStr = json.toString();
// 格式: {"ret":1,"start":"...","end":"...","country":"中國","province":"廣東","city":"廣州","district":"","isp":"","type":"","desc":""}
IpLocation loc = new IpLocation();
loc.setCountry(extractJsonValue(jsonStr, "country"));
loc.setProvince(extractJsonValue(jsonStr, "province"));
loc.setCity(extractJsonValue(jsonStr, "city"));
loc.setIsp(extractJsonValue(jsonStr, "isp"));
loc.setRawJson(jsonStr);
return loc;
} catch (IOException e) {
throw new RuntimeException("新浪 IP 查詢失敗", e);
}
}
private String extractJsonValue(String json, String key) {
String pattern = "\"" + key + "\":\"";
int start = json.indexOf(pattern);
if (start == -1) return "";
start += pattern.length();
int end = json.indexOf("\"", start);
return end > start ? json.substring(start, end) : "";
}
public static class IpLocation {
private String country, province, city, isp, rawJson;
// Getter/Setter/toString...
}
}
@Service
public class ProductionIpLocationService {
private final List providers = Arrays.asList(
new SinaIpLocationProvider(),
new TaobaoIpLocationProvider(),
new QqIpLocationProvider()
);
private final Cache cache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(Duration.ofHours(24))
.build();
@Async
public CompletableFuture getLocationAsync(String ip) {
return CompletableFuture.supplyAsync(() -> getLocation(ip));
}
public IpLocation getLocation(String ip) {
// 1. 緩存查找
IpLocation cached = cache.getIfPresent(ip);
if (cached != null) return cached;
// 2. 多源嘗試
for (IpLocationProvider provider : providers) {
try {
IpLocation loc = provider.getLocation(ip);
if (loc != null && loc.isValid()) {
cache.put(ip, loc);
return loc;
}
} catch (Exception e) {
log.warn("Provider {} 失敗: {}", provider.getClass().getSimpleName(), e.getMessage());
}
}
return IpLocation.unknown(ip);
}
interface IpLocationProvider {
IpLocation getLocation(String ip);
}
}
IP 地理位置查詢推薦優先使用新浪/淘寶 JSON API,結合緩存、異步、多源容錯構建生產級服務。高併發/內網場景建議採用離線數據庫(ip2region/MaxMind)消除網絡依賴、實現微秒級查詢。關鍵在於:超時控制、編碼正確、緩存命中率、熔斷機制、合規脫敏。