首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 淘寶 IP 地址查詢類:基於淘寶開放接口獲取地理位置信息

     編輯:PHP 相關知識

在評論系統、用戶畫像、風控反欺詐等場景中,根據 IP 獲取地理位置(國家、省份、城市、ISP)是基礎需求。淘寶提供免費的 IP 地址庫查詢接口,本文封裝為開箱即用的 PHP 類。

淘寶 IP 接口說明

  • 接口地址:https://ip.taobao.com/service/getIpInfo.php?ip=IP地址
  • 請求方式:HTTP GET
  • 返回格式:JSON
  • 頻率限制:官方建議 < 10 QPS,高並發需自建庫或付費服務
  • 返回字段:country、region、city、isp、area 等

完整查詢類實現

<?php
class TaobaoIPQuery {
    private $m_ip;
    private $m_content; // 解析後的 JSON 對象
    
    /**
     * 構造函數:初始化並發起查詢
     * @param string $ip 目標 IP 地址,為空則拋出異常
     */
    public function __construct($ip) {
        if (isset($ip)) {
            $this->m_ip = $ip;
        } else {
            $this->m_ip = "";
        }
        
        if (!empty($this->m_ip)) {
            $url_handle = curl_init();
            curl_setopt($url_handle, CURLOPT_URL, "https://ip.taobao.com/service/getIpInfo.php?ip=" . $this->m_ip);
            curl_setopt($url_handle, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($url_handle, CURLOPT_TIMEOUT, 5);      // 連接超時 5 秒
            curl_setopt($url_handle, CURLOPT_CONNECTTIMEOUT, 3); // 總超時 3 秒
            $this->m_content = curl_exec($url_handle);
            $curl_errno = curl_errno($url_handle);
            $curl_error = curl_error($url_handle);
            curl_close($url_handle);
            
            if ($curl_errno) {
                throw new Exception("cURL error ({$curl_errno}): {$curl_error}");
            }
            
            if ($this->m_content) {
                $this->m_content = json_decode($this->m_content);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    throw new Exception("JSON 解析失敗: " . json_last_error_msg());
                }
                
                // code=0 成功,code=1 失敗(如 IP 格式錯誤、頻率超限)
                if (isset($this->m_content->code) && $this->m_content->code == 1) {
                    throw new Exception("查詢失敗: " . ($this->m_content->msg ?? '未知錯誤'));
                }
            } else {
                throw new Exception("接口返回空內容");
            }
        } else {
            throw new Exception("IP 地址不能為空");
        }
    }
    
    // Getter 方法
    public function getRegion() { return $this->m_content->data->region ?? ''; }    // 省份
    public function getCity()   { return $this->m_content->data->city ?? ''; }      // 城市
    public function getCountry() { return $this->m_content->data->country ?? ''; }   // 國家
    public function getIsp()    { return $this->m_content->data->isp ?? ''; }       // 運營商
    public function getArea()   { return $this->m_content->data->area ?? ''; }      // 區域(如華東、內網)
    public function getCounty() { return $this->m_content->data->county ?? ''; }    // 區/縣
    
    // 獲取完整數據
    public function getAll() { return $this->m_content->data ?? null; }
}
?>

使用示例

<?php
require_once 'TaobaoIPQuery.php';

try {
    // 獲取客戶端真實 IP(考慮代理)
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];
    // 取第一個非私有 IP
    $ip_list = array_map('trim', explode(',', $ip));
    $real_ip = '127.0.0.1';
    foreach ($ip_list as $client_ip) {
        if (!filter_var($client_ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
            continue;
        }
        $real_ip = $client_ip;
        break;
    }
    
    $ipquery = new TaobaoIPQuery($real_ip);
    
    echo "IP: {$real_ip}\n";
    echo "國家: " . $ipquery->getCountry() . "\n";
    echo "省份: " . $ipquery->getRegion() . "\n";
    echo "城市: " . $ipquery->getCity() . "\n";
    echo "區縣: " . $ipquery->getCounty() . "\n";
    echo "運營商: " . $ipquery->getIsp() . "\n";
    
    // 完整數據
    // print_r($ipquery->getAll());
    
} catch (Exception $e) {
    error_log("IP 查詢異常: " . $e->getMessage());
    // 降級處理:使用本地 GeoIP 庫或默認值
    $region = '未知';
}
?>

生產環境優化建議

1. 本地緩存(Redis/Memcached)大幅降低延遲與外部依賴

<?php
class CachedTaobaoIPQuery extends TaobaoIPQuery {
    private $redis;
    private $ttl = 86400 * 30; // 30 天緩存
    
    public function __construct($ip, $redis) {
        $this->redis = $redis;
        $cache_key = "ip_geo:{$ip}";
        
        // 嘗試從緩存讀取
        $cached = $this->redis->get($cache_key);
        if ($cached) {
            $this->m_content = json_decode($cached);
            $this->m_ip = $ip;
            return; // 直接返回,不發起 HTTP 請求
        }
        
        // 緩存未命中,調用父類構造函數查詢
        try {
            parent::__construct($ip);
            // 寫入緩存
            if ($this->m_content) {
                $this->redis->setex($cache_key, $this->ttl, json_encode($this->m_content));
            }
        } catch (Exception $e) {
            // 查詢失敗時,嘗試讀取過期緩存作為降級
            $stale = $this->redis->get("{$cache_key}:stale");
            if ($stale) {
                $this->m_content = json_decode($stale);
            }
            throw $e;
        }
    }
}
?>

2. 異步預熱與批量查詢

<?php
// 批量查詢(需自建 IP 庫或付費 API,淘寶接口不支援批量)
// 異步預熱:定時任務掃描活躍用戶 IP,提前查詢緩存
function warmupIpCache($redis, $ip_list) {
    $pipe = $redis->multi();
    foreach ($ip_list as $ip) {
        $pipe->get("ip_geo:{$ip}");
    }
    $results = $pipe->exec();
    
    $missed = [];
    foreach ($results as $i => $result) {
        if (!$result) $missed[] = $ip_list[$i];
    }
    
    // 併發查詢缺失的 IP(使用 curl_multi 或 Swoole 協程)
    // 這裡簡單示意
    foreach ($missed as $ip) {
        // 異步投遞到查詢佇列...
    }
}
?>

3. 降級策略:本地 GeoIP2 數據庫

<?php
// composer require geoip2/geoip2:~2.0
// 下載 GeoLite2-City.mmdb: https://dev.maxmind.com/geoip/geoip2/geolite2/
use GeoIp2\Database\Reader;

class FallbackGeoIP {
    private $reader;
    
    public function __construct($db_path = '/usr/share/GeoIP/GeoLite2-City.mmdb') {
        if (!file_exists($db_path)) {
            throw new Exception("GeoIP 數據庫文件不存在: {$db_path}");
        }
        $this->reader = new Reader($db_path);
    }
    
    public function query($ip) {
        try {
            $record = $this->reader->city($ip);
            return [
                'country' => $record->country->isoCode,
                'region'  => $record->mostSpecificSubdivision->name,
                'city'    => $record->city->name,
                'isp'     => $record->traits->isp ?? '',
            ];
        } catch (Exception $e) {
            return null;
        }
    }
}
?>

完整調用鏈路設計

<?php
function getIpLocation($ip, $redis = null) {
    // 1. 嘗試 Redis 緩存
    if ($redis) {
        $cached = $redis->get("ip_geo:{$ip}");
        if ($cached) return json_decode($cached, true);
    }
    
    // 2. 嘗試淘寶接口
    try {
        $query = new TaobaoIPQuery($ip);
        $data = [
            'country' => $query->getCountry(),
            'region'  => $query->getRegion(),
            'city'    => $query->getCity(),
            'isp'     => $query->getIsp(),
        ];
        if ($redis) $redis->setex("ip_geo:{$ip}", 86400*30, json_encode($data));
        return $data;
    } catch (Exception $e) {
        error_log("淘寶 IP 查詢失敗: " . $e->getMessage());
    }
    
    // 3. 降級:本地 GeoIP2
    try {
        $geoip = new FallbackGeoIP();
        $data = $geoip->query($ip);
        if ($data && $redis) $redis->setex("ip_geo:{$ip}", 86400*7, json_encode($data));
        return $data;
    } catch (Exception $e) {
        error_log("本地 GeoIP 查詢失敗: " . $e->getMessage());
    }
    
    // 4. 終極降級
    return ['country'=>'', 'region'=>'', 'city'=>'', 'isp'=>''];
}
?>

注意事項與避坑指南

  1. HTTPS 站點調用 HTTP 接口會被瀏覽器攔截:服務端 PHP 請求不受影響,但前端 AJAX 直接調用會報 Mixed Content 錯誤
  2. 頻率限制嚴格:單機超 10 QPS 會被封禁,集群環境需統一代理或本地緩存
  3. 內網/保留地址查詢無意義:10.x、172.16-31.x、192.168.x、127.x 應直接返回「內網」
  4. IPv6 支援有限:淘寶接口對 IPv6 支援不穩定,建議優先處理 IPv4
  5. 數據準確性:免費庫精度到城市級,區/縣、街道級準確率較低,嚴格業務請用付費庫

合理使用緩存、降級、異步預熱,能構建高可用、低延遲的 IP 地理定位服務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved