最简单方案是用 file_get_contents 配合 stream_context_create 设置超时和 ua,需判 false 再验 json;进阶用 curl 并发、校验证书;务必字段判空、限流缓存、离线库定期更新。

用 file_get_contents 调第三方 IP 查询接口最简单,但得自己处理超时和错误
PHP 做 IP 归属地查询,本质就是发 HTTP 请求拿 JSON 或纯文本响应。最轻量的做法是直接用 file_get_contents 配合 stream_context_create 控制超时和 UA,不用装额外包。
常见错误现象:file_get_contents(): failed to open stream: Connection timed out —— 默认没设超时,遇到第三方接口卡住就死等;或者返回空字符串却没检查 $result === false 就直接 json_decode,结果报 json_last_error() 是 4(语法错误)。
- 必须用
stream_context_create设timeout(建议 3–5 秒)和user_agent(有些 API 拒绝空 UA) - 调用后先判断是否为
false,再检查json_last_error() === JSON_ERROR_NONE - 别硬编码 URL,把 API 地址、key(如有)、IP 参数抽成变量,方便换服务商
$opts = [
'http' => [
'method' => 'GET',
'timeout' => 4,
'header' => "User-Agent: PHP-IP-Lookup\r\n"
]
];
$result = file_get_contents('https://ip.taobao.com/outGetIpInfo?ip=1.2.3.4&accessKey=xxx', false, stream_context_create($opts));
if ($result === false) {
// 处理请求失败
} else {
$data = json_decode($result, true);
if (json_last_error() !== JSON_ERROR_NONE) {
// 处理解析失败
}
}
用 cURL 更稳,尤其要并发查多个 IP 或需要 HTTPS 证书校验时
当你要批量查几十个 IP,或者目标 API 强制校验证书(比如某些新版腾讯云、阿里云接口),cURL 是更可控的选择。它能复用连接、设重试、精细控制 SSL 行为。
容易踩的坑:CURLOPT_SSL_VERIFYPEER 设成 false 图省事,上线后被安全扫描报高危;或忘了 CURLOPT_RETURNTRANSFER => true,结果 curl_exec 直接输出内容到页面,导致 JSON 解析失败。
立即学习“PHP免费学习笔记(深入)”;
- 必须设
CURLOPT_RETURNTRANSFER => true,否则返回true而不是响应体 - 生产环境禁用
CURLOPT_SSL_VERIFYPEER => false,改用CURLOPT_CAINFO指向系统 CA 包(如/etc/ssl/certs/ca-certificates.crt) - 查多个 IP 时,用
curl_multi_init并发,别写 for 循环串行请求
别信“免费无限调用”的 API,注意频率限制和返回字段差异
淘宝 IP 库(ip.taobao.com)已下线;现在主流有聚合数据、腾讯云 IP 定位、百度地图 IP 定位等。它们返回结构不统一:有的用 data.country,有的用 country_name;有的城市字段叫 city,有的叫 region。
典型错误:直接写 $data['data']['city'],结果换一家 API 就 Notice:Undefined index;或者没看文档,默认认为所有 IP 都能返回精确到区县,实际很多只到市级,甚至返回“海外”或空字符串。
- 始终用
isset()或??判断字段是否存在,别假设结构 - 留意各家的 QPS 限制(比如聚合数据免费版 100 次/天,腾讯云按量计费但有 10 次/秒默认配额)
- 缓存结果很关键:对同一 IP,30 分钟内重复查,直接读本地缓存(文件或 Redis),避免触发限流
本地离线库适合高频低延迟场景,但得定期更新 IP 数据库
如果每秒要查上百次,又不能接受第三方 API 的延迟和不稳定,就得上离线方案,比如 maxmind-db/reader + GeoLite2 City 数据库。
问题在于:GeoLite2 免费版每月才更新一次,IP 段变动后归属地可能不准;而且 PHP 加载大数据库文件(百 MB 级)有内存开销,新手常忘记用 __destruct 或 unset 释放 Reader 实例。
- 用 Composer 装
maxmind-db/reader,别手动解析 mmdb 文件 - 数据库文件别放 webroot 下,防止被直接下载;路径用绝对路径,避免 include_path 误判
- 更新脚本要加锁(比如用
flock),否则多进程同时更新会损坏数据库文件
离线库最大的隐性成本不是代码,是维护——没人盯着更新日期,半年后查出的“北京朝阳区”其实是旧数据,而真实 IP 已分给河北某 IDC。











