全方位站长技能、SEO优化学习平台
当前位置:网站首页 > PHP笔记 > 正文

IP位置查询实战案例:PHP如何使用纯真IP库实现IP定位和位置查询?

作者:admin发布时间:2024-06-29分类:PHP笔记浏览:11602


温馨提示:手机扫码可阅读当前文章!
文章简介:利用纯真IP库查询IP的位置是十分方便的,只是准确率和详细程度还有待提高。既然如此,我为啥还要使用纯真IP库呢?当然是因为网络上纯真IP离线库够丰富,对于用来做教程和写案例文章十分方便,如果你要正式使用纯真IP库,还是需要去纯真官网购买最新...

利用纯真IP库查询IP的位置是十分方便的,只是准确率和详细程度还有待提高。既然如此,我为啥还要使用纯真IP库呢?当然是因为网络上纯真IP离线库够丰富,对于用来做教程和写案例文章十分方便,如果你要正式使用纯真IP库,还是需要去纯真官网购买最新的离线库,保证查询数据的准确度和全面性。


什么是纯真IP呢?这里我简单介绍一下:纯真自2005年起一直为广大社区用户提供社区版IP地址库,只要获得纯真的授权就能免费使用,并不断获取后续更新的版本。纯真除了免费的社区版IP库外,还提供数据更加准确、服务更加周全的商业版IP地址查询数据。纯真围绕IP地址,基于 网络空间拓扑测绘 + 移动位置大数据 方案,对IP地址定位、IP网络风险、IP使用场景、IP网络类型、秒拨侦测、VPN侦测、代理侦测、爬虫侦测、真人度等均有近20年丰富的数据沉淀。


如果我们已经获得纯真IP离线库qqwry.dat,我们应该怎样使用呢?因为以前没有接触过与dat数据库相关的使用经历,一开始我用PHP直接打开,发现读取出来的内容是乱码,根本不能用!于是在网上找教程,发现很多博主都分享了PHP使用纯真IP离线库qqwry.dat的类,我也分享给大家,免得再去网上查找:


<?php
class caozha_ip {
  var $dat_config = "qqwry.dat";   //纯真数据库,这里注意,根据你的数据库存放位置不同,把这个数据库进行引入。
  private $fp;//IP库文件指针 resource
  private $firstip;//第一条IP记录的偏移地址 int
  private $lastip;//最后一条IP记录的偏移地址 int
  private $totalip;//IP记录的总条数(不包含版本信息记录) int
  /**
  * 构造函数,打开.Dat文件并初始化类中的信息
  *
  * @param string $filename
  * @return IpLocation
  */
  public function __construct() {
    $filename=$this->dat_config;
    $this->fp = 0;
    if (($this->fp = fopen($filename, 'rb',1)) !== false) {
      $this->firstip = $this->getlong();
      $this->lastip = $this->getlong();
      $this->totalip = ($this->lastip - $this->firstip) / 7;//注册析构函数,使其在程序执行结束时执行
      register_shutdown_function(array(&$this, '__destruct'));
    }
  }
  /**
  * 析构函数,用于在页面执行结束后自动关闭打开的文件。
  */
  public function __destruct() {
    if ($this->fp) {
      fclose($this->fp);
    }
    $this->fp = 0;
  }
  /**
  * 返回读取的长整型数
  *
  * @access private
  * @return int
  */
  private function getlong() {//将读取的little-endian编码的4个字节转化为长整型数
  $result = unpack('Vlong', fread($this->fp, 4));
  return $result['long'];
  }
  /**
  * 返回读取的3个字节的长整型数
  *
  * @access private
  * @return int
  */
  private function getlong3() {//将读取的little-endian编码的3个字节转化为长整型数
  $result = unpack('Vlong', fread($this->fp, 3).chr(0));
  return $result['long'];
  }
  /**
  * 返回压缩后可进行比较的IP地址
  *
  * @access private
  * @param string $ip
  * @return string
  */
  private function packip($ip) {// 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,// 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
  return pack('N', intval(ip2long($ip)));
  }
  /**
  * 返回读取的字符串
  *
  * @access private
  * @param string $data
  * @return string
  */
  private function getstring($data = "") {
  $char = fread($this->fp, 1);
  while (ord($char) > 0) {// 字符串按照C格式保存,以\0结束
  $data .= $char;// 将读取的字符连接到给定字符串之后
  $char = fread($this->fp, 1);
  }
  return $data;
  }
  /**
  * 返回地区信息
  *
  * @access private
  * @return string
  */
  private function getarea() {
    $byte = fread($this->fp, 1);// 标志字节
    switch (ord($byte)) {
    case 0:// 没有区域信息
      $area = "";
      break;
    case 1:
    case 2:// 标志字节为1或2,表示区域信息被重定向
      fseek($this->fp, $this->getlong3());
      $area = $this->getstring();
      break;
    default:// 否则,表示区域信息没有被重定向
      $area = $this->getstring($byte);
      break;
    }
    return $area;
  }
  /**
  * 根据所给 IP 地址或域名返回所在地区信息
  *
  * @access public
  * @param string $ip
  * @return array
  */
  public function get($ip) {
    if (!$this->fp) return null;// 如果数据文件没有被正确打开,则直接返回空
    $location['ip'] = gethostbyname($ip);   // 将输入的域名转化为IP地址
    $ip = $this->packip($location['ip']);   // 将输入的IP地址转化为可比较的IP地址
    // 不合法的IP地址会被转化为255.255.255.255// 对分搜索
    $l = 0;// 搜索的下边界
    $u = $this->totalip;// 搜索的上边界
    $findip = $this->lastip;// 如果没有找到就返回最后一条IP记录(QQWry.Dat的版本信息)
    while ($l <= $u) {// 当上边界小于下边界时,查找失败
      $i = floor(($l + $u) / 2); // 计算近似中间记录
      fseek($this->fp, $this->firstip + $i * 7);
      $beginip = strrev(fread($this->fp, 4));// 获取中间记录的开始IP地址// strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式// 以便用于比较,后面相同。
      if ($ip < $beginip) {// 用户的IP小于中间记录的开始IP地址时
        $u = $i - 1;// 将搜索的上边界修改为中间记录减一
      }else{
        fseek($this->fp, $this->getlong3());
        $endip = strrev(fread($this->fp, 4));   // 获取中间记录的结束IP地址
        if ($ip > $endip) {// 用户的IP大于中间记录的结束IP地址时
          $l = $i + 1;// 将搜索的下边界修改为中间记录加一
        }else{// 用户的IP在中间记录的IP范围内时
          $findip = $this->firstip + $i * 7;
          break;// 则表示找到结果,退出循环
        }
      }
    }//获取查找到的IP地理位置信息
    fseek($this->fp, $findip);
    $location['beginip'] = long2ip($this->getlong());   // 用户IP所在范围的开始地址
    $offset = $this->getlong3();
    fseek($this->fp, $offset);
    $location['endip'] = long2ip($this->getlong());// 用户IP所在范围的结束地址
    $byte = fread($this->fp, 1);// 标志字节
    switch (ord($byte)) {
    case 1:// 标志字节为1,表示国家和区域信息都被同时重定向
      $countryOffset = $this->getlong3();// 重定向地址
      fseek($this->fp, $countryOffset);
      $byte = fread($this->fp, 1);// 标志字节
      switch (ord($byte)) {
      case 2:// 标志字节为2,表示国家信息又被重定向
        fseek($this->fp, $this->getlong3());
        $location['country'] = $this->getstring();
        fseek($this->fp, $countryOffset + 4);
        $location['area'] = $this->getarea();
        break;
      default:// 否则,表示国家信息没有被重定向
        $location['country'] = $this->getstring($byte);
        $location['area'] = $this->getarea();
        break;
      }
      break;
    case 2:// 标志字节为2,表示国家信息被重定向
      fseek($this->fp, $this->getlong3());
      $location['country'] = $this->getstring();
      fseek($this->fp, $offset + 8);
      $location['area'] = $this->getarea();
      break;
    default:// 否则,表示国家信息没有被重定向
      $location['country'] = $this->getstring($byte);
      $location['area'] = $this->getarea();
      break;
    }
    if ($location['country'] == " CZ88.NET") { // CZ88.NET表示没有有效信息
      $location['country'] = "未知";
    }
    if ($location['area'] == " CZ88.NET") {
      $location['area'] = "";
    }
    $location['country']=iconv('gbk', 'utf-8', $location['country']);
    $location['area']=iconv('gbk', 'utf-8', $location['area']);
    return $location;
  }  
  /*
  方法:IP转地址
  参数:
  $ip 要查询的IP地址,为空时自动获取本机IP
  $format 输出格式,可选值:js、json、jsonp、text、xml
  $callback 回调函数名,当$format=jsonp时设置
  */
  function ip_to_address( $format = "json", $ip = "", $callback = "" ) {
    if ( !$ip ) {
      $ip = $this->getip();
    }
    
    $IP_addr=$this->get($ip);    
    $IP_addr_new = $this->splitAddress( $IP_addr[ "country" ] );
    
    
    $list_arr = array(
      "ip" => $ip,
      "ip_start" => $IP_addr[ "beginip" ],
      "ip_end" => $IP_addr[ "endip" ],
      "address" => $IP_addr[ "country" ],
      "location" => $IP_addr[ "area" ],
      "country" => $IP_addr_new[ "country" ],
      "province" => $IP_addr_new[ "province" ],
      "city" => $IP_addr_new[ "city" ],
      "area" => $IP_addr_new[ "area" ]
    );
    return $this->out_format( $list_arr, $format, $callback );
  }
  function out_format( $list, $format, $callback ) { //按格式输出数据
    switch ( $format ) {
      case "js":
        return "var ip_info = " . json_encode( $list ) . ";";
        break;
      case "json":
        return json_encode( $list );
        break;
      case "jsonp":
        return $callback . "(" . json_encode( $list ) . ");";
        break;
      case "text":
        return implode( "|", $list );
        //return "ip:" . $list[ "ip" ] . ",ip_start:" . $list[ "ip_start" ] . ",ip_end:" . $list[ "ip_end" ] . ",address:" . $list[ "address" ] . ",location:" . $list[ "location" ] . ",country:" . $list[ "country" ] . ",province:" . $list[ "province" ] . ",city:" . $list[ "city" ] . ",area:" . $list[ "area" ];
        break;
      case "xml":
        return $this->arrayToXml( $list );
        break;
      default:
        return json_encode( $list );
    }
  }
  function arrayToXml( $arr ) { //数组转XML
    $xml = "<root>";
    foreach ( $arr as $key => $val ) {
      if ( is_array( $val ) ) {
        $xml .= "<" . $key . ">" . $this->arrayToXml( $val ) . "</" . $key . ">";
      } else {
        $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
      }
    }
    $xml .= "</root>";
    return $xml;
  }
  function getip() { //获取客户端IP
    if ( $_SERVER[ "HTTP_CDN_SRC_IP" ] ) { //获取网宿CDN真实客户IP
      return $this->replace_ip( $_SERVER[ "HTTP_CDN_SRC_IP" ] );
    }
    if ( $_SERVER[ "HTTP_X_FORWARDED_FOR" ] ) { //获取网宿、阿里云真实客户IP,参考:https://help.aliyun.com/knowledge_detail/40535.html
      return $this->replace_ip( $_SERVER[ "HTTP_X_FORWARDED_FOR" ] );
    }
    if ( $_SERVER[ "HTTP_CLIENT_IP" ] ) {
      return $_SERVER[ "HTTP_CLIENT_IP" ];
    }
    if ( $_SERVER[ "HTTP_X_FORWARDED" ] ) {
      return $_SERVER[ "HTTP_X_FORWARDED" ];
    }
    if ( $_SERVER[ "HTTP_FORWARDED_FOR" ] ) {
      return $_SERVER[ "HTTP_FORWARDED_FOR" ];
    }
    if ( $_SERVER[ "HTTP_FORWARDED" ] ) {
      return $_SERVER[ "HTTP_FORWARDED" ];
    }
    $httpip = $_SERVER[ 'REMOTE_ADDR' ];
    if ( !preg_match( "/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/", $httpip ) ) {
      $httpip = "127.0.0.1";
    }
    return $httpip;
  }
  function replace_ip( $ip ) {
    if ( !$ip ) {
      return "";
    }
    $httpip_array = explode( ",", $ip );
    if ( $httpip_array[ 0 ] ) {
      return $httpip_array[ 0 ];
    } else {
      return $ip;
    }
  }    
  function splitAddress( $address ) { //从IP库的地址中提取省市区等数据
  preg_match( '/(.*?(省|市|西藏|内蒙古|新疆|广西|宁夏|香港|澳门))/', $address, $matches );
  if ( count( $matches ) > 1 ) {
    $province = $matches[ count( $matches ) - 2 ];
    $address = str_replace( $province, '', $address );
  }
  preg_match( '/(.*?(市|自治州|地区|区划|县))/', $address, $matches );
  if ( count( $matches ) > 1 ) {
    $city = $matches[ count( $matches ) - 2 ];
    $address = str_replace( $city, '', $address );
  }
  preg_match( '/(.*?(市|区|县|镇|乡|街道))/', $address, $matches );
  if ( count( $matches ) > 1 ) {
    $area = $matches[ count( $matches ) - 2 ];
    $address = str_replace( $area, '', $address );
  }
  if ( $province ) {
    $country = "中国";
  } else {
    preg_match( '/^(.*?IANA.*?)$/', $address, $matches );
    if ( count( $matches ) <= 1 ) {
      $country = $address;
    }
  }
  unset( $matches );
  return array( 'country' => $country,
    'province' => isset( $province ) ? $province : '',
    'city' => isset( $city ) ? $city : '',
    'area' => isset( $area ) ? $area : '' );
  }
}

代码来自Git仓库: https://gitee.com/caozha/caozha-ip  

该类的使用方法:


$ip='127.0.0.1';
$ipinfo=new caozha_ip();
$address=$caozha->ip_to_address("json",$ip);

本人根据该类和纯真IP离线库qqwry.dat开发成zblog插件,该插件是一个简单的IP定位查询工具,主要是用于大家对本文内容的进行验证。本站提供的IP查询工具和api接口服务都是不限制的免费使用,你只需要通过URL请求传入相关参数甚至可以直接在站外调用,不需要进入本页手动生成!由于使用的是网络上的免费资源开发二次,所以也是永久免费服务,理论上只要本站还能正常访问,你通过URL请求传入参数调用IP查询api接口,即可获得IP的位置数据。本站不倒、服务不倒!不过因为使用的是纯真IP的免费版,可能准确率和定位精度不够理想,但是不影响大家体验测试!测试地址:https://www.wsbjw.cn/?wensha_ip=index


IP位置查询实战案例:PHP如何使用纯真IP库实现IP定位和位置查询?  第1张


欢迎您,来自美国–弗吉尼亚州–劳登县–阿什本的朋友,您的IP:3.236.112.101,您的网络:Amazon数据中心


PHP笔记排行
随机推荐
猜你喜欢

服务热线

1888888888

要发发发发发发

站长微信公众号

站长微信公众号

分享:

支付宝

微信