把纯真IP数据库中的记录导入Mysql数据库的PHP脚本

        最近在带一个BI项目,其中要展示用户注册、登陆、充值的地区分布。技术的原始数据中保存的是IP地址,然后通过php查询脚本从qqwry.dat文件中查出某个ip对应的地区。

        技术的代码更适合处理一条一条的记录。而我们做数据分析,更希望是把整个qqwry.dat文件中的记录解析出来放到数据库中,这样使用起来会比较方便,特别是批处理的时候。

        本文是在技术给的IP查询脚本的基础上写的遍历程序。关于qqwry.dat文件的结构,可以参考这几篇文章:

1.《关于QQWry.dat格式》,原文链接:http://blog.csdn.net/cnss/article/details/77628

2.《纯真IP数据库格式详解》,原文链接:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html

        这两个文档都非常重要,个人感觉,把结构弄清楚了,就比较好处理了。废话不多说,上代码:

<?php
function read_qqip($qq_datafile)
{
    if(!$fd = @fopen($qq_datafile, 'rb')){
    return 'Invalid IP data file';
  }
    
  //获取文件的前8个字节
  if(!($DataBegin = fread($fd, 4)) || !($DataEnd = fread($fd, 4)) ) return;
  //$ipbegin指向第一个起始IP的位置(索引区)
  @$ipbegin = implode('', unpack('L', $DataBegin)); 
  if($ipbegin < 0) $ipbegin += pow(2, 32);
  //$ipend指向最后一个起始IP的位置(索引区)
  @$ipend = implode('', unpack('L', $DataEnd));
  if($ipend < 0) $ipend += pow(2, 32);
  //索引区每条记录的长度为7个字节,$ipAllNum表示的是IP段的个数
  $ipAllNum = ($ipend - $ipbegin) / 7 + 1;  
  
  //在起始IP区进行索引
  $result = array(array());
  for($i = 0; $i < $ipAllNum; $i++)
  {
    fseek($fd, $ipbegin + 7 * $i);
    $ip_start = fread($fd, 4);  //读取IP段的起始IP的二进制串
    
    if(strlen($ip_start) < 4){
          fclose($fd);
          return 'System Error';
    }
    $ip_start = implode('', unpack('L', $ip_start));
    if($ip_start < 0) $ip_start += pow(2, 32);  
    $result[$i]['IP_START'] = $ip_start;   //获取IP段的起始IP值
    
    $DataSeek = fread($fd, 3);  //寻找结束IP
    if(strlen($DataSeek) < 3) {
      fclose($fd);
      return 'System Error';
    }
    $DataSeek = implode('', unpack('L', $DataSeek.chr(0)));
    fseek($fd, $DataSeek);
    $ip_end = fread($fd, 4);   //结束IP二进制码
    if(strlen($ip_end) < 4) {
      fclose($fd);
      return 'System Error';
    }
    $ip_end = implode('', unpack('L', $ip_end)); //结束IP值
    if($ip_end < 0) $ip_end += pow(2, 32);    
    $result[$i]['IP_END'] = $ip_end;  //读取结束IP值  
    
    $ipAddr1 = '';
    $ipAddr2 = '';
        //下面开始读取国家和地区信息
        $ipFlag = fread($fd, 1);  //指向国家串前的一个字节
      if($ipFlag == chr(1)) {  //为0x01表明国家、地区与前面的IP信息重复
        $ipSeek = fread($fd, 3);  //国家、地区字符串的偏移量
        if(strlen($ipSeek) < 3) {
          fclose($fd);
          return 'System Error';
        }
        $ipSeek = implode('', unpack('L', $ipSeek.chr(0)));
        fseek($fd, $ipSeek);   //返回到国家、地区偏移量处
        $ipFlag = fread($fd, 1);
      }
      if($ipFlag == chr(2)) { //为0x02,表明国家串与前面的国家或地区重复
        $AddrSeek = fread($fd, 3);  //国家串的偏移量
        if(strlen($AddrSeek) < 3) {
          fclose($fd);
          return 'System Error';
        }
        $ipFlag = fread($fd, 1); //读取地区串前的第一个字节
        if($ipFlag == chr(2)) {  //出现0x02说明地区串与前面的国家或地区串重复
          $AddrSeek2 = fread($fd, 3);   //地区串的偏移量
          if(strlen($AddrSeek2) < 3) {
            fclose($fd);
            return 'System Error';
          }
          $AddrSeek2 = implode('', unpack('L', $AddrSeek2.chr(0)));
          fseek($fd, $AddrSeek2);
        } else {
          fseek($fd, -1, SEEK_CUR);
        }
        while(($char = fread($fd, 1)) != chr(0))
        $ipAddr2 .= $char;
        $AddrSeek = implode('', unpack('L', $AddrSeek.chr(0)));
        fseek($fd, $AddrSeek);
        while(($char = fread($fd, 1)) != chr(0))
        $ipAddr1 .= $char;
      }else {//国家串不与前面重复
        fseek($fd, -1, SEEK_CUR);
        while(($char = fread($fd, 1)) != chr(0))
        $ipAddr1 .= $char;  //读取国家串
        $ipFlag = fread($fd, 1);
        if($ipFlag == chr(2)) {  //地区串与前面的串重复
          $AddrSeek2 = fread($fd, 3);
          if(strlen($AddrSeek2) < 3) {
            fclose($fd);
            return 'System Error';
          }
          $AddrSeek2 = implode('', unpack('L', $AddrSeek2.chr(0)));
          fseek($fd, $AddrSeek2);
        } else {
          fseek($fd, -1, SEEK_CUR);
        }
        while(($char = fread($fd, 1)) != chr(0))
        $ipAddr2 .= $char;  //地区串
      }
      if(preg_match('/http/i', $ipAddr2)) {
        $ipAddr2 = '';
      }
      
      $ipaddr = "$ipAddr1";
      $ipaddr = preg_replace('/CZ88.NET/is', '', $ipaddr);
      $ipaddr = preg_replace('/^s*/is', '', $ipaddr);
      $ipaddr = preg_replace('/s*$/is', '', $ipaddr);
      if(preg_match('/http/i', $ipaddr) || $ipaddr == '') {
        $ipaddr = 'Unknown';
      }
    $ipaddr = mb_convert_encoding($ipaddr, "utf-8", "gb2312"); 
    $ipAddr2 = mb_convert_encoding($ipAddr2, "utf-8", "gb2312");
      $result[$i]['BIG_AREA'] = $ipaddr;
    $result[$i]['SMALL_AREA'] = $ipAddr2;
    //echo $result[$i]['BIG_AREA'].'---'.$result[$i]['SMALL_AREA'].'<br>';
    
  }
  return $result;
}
?>
  

        需要说明的是,上面的代码并非100%原创,而是在技术给的代码的基础上修改出来的,在此感谢相关人员。

        获取$result之后,只需要把$result写入相应的数据表即可。导入之后的结果如下:

        image

原文地址:https://www.cnblogs.com/huake/p/3495231.html