PHP转换IP地址到真实地址的方法详解


Posted in PHP onJune 09, 2013

想要把IPv4地址转为真实的地址,肯定要参考IP数据库,商业的IP数据库存储在关系型数据库中,查询和使用都非常方便,但是成本不是个人和小公 司愿意承受的,所以简单应用的思路就是利用一些免费的IP数据库或者一些大网站提供的查询API,他们的数据量足够我们使用了。
1. 利用纯真IP数据库
利用本地的QQWry.Dat文件,优点是查询速度非常快,缺点是数据库文件要放在自己的空间内并且要偶尔更新数据库。时间关系废话不多说,下面是 使用这个文件的函数,如果是在WordPress里面使用这个功能,把下面的代码写入主题下面的functions.php里面,然后在 comments-list的输出<?php echo convertip(get_comment_author_ip()); ?>即可;如果是其他程序引用,输入一个有效的IPv4地址就可以得到一个真实的地址。

function convertip($ip) {
    //IP数据文件路径
    $dat_path = 'QQWry.Dat';
    //检查IP地址
    //if(!preg_match("/^d{1,3}.d{1,3}.d{1,3}.d{1,3}$/", $ip)) {
    //    return 'IP Address Error';
    //}
    //打开IP数据文件
    if(!$fd = @fopen($dat_path, 'rb')){
        return 'IP date file not exists or access denied';
    }
    //分解IP进行运算,得出整形数
    $ip = explode('.', $ip);
    $ipNum = $ip[0] * 16777216 + $ip[1] * 65536 + $ip[2] * 256 + $ip[3];
    //获取IP数据索引开始和结束位置
    $DataBegin = fread($fd, 4);
    $DataEnd = fread($fd, 4);
    $ipbegin = implode('', unpack('L', $DataBegin));
    if($ipbegin < 0) $ipbegin += pow(2, 32);
    $ipend = implode('', unpack('L', $DataEnd));
    if($ipend < 0) $ipend += pow(2, 32);
    $ipAllNum = ($ipend - $ipbegin) / 7 + 1;
    $BeginNum = 0;
    $EndNum = $ipAllNum;
    //使用二分查找法从索引记录中搜索匹配的IP记录
    while($ip1num>$ipNum || $ip2num<$ipNum) {
        $Middle= intval(($EndNum + $BeginNum) / 2);
        //偏移指针到索引位置读取4个字节
        fseek($fd, $ipbegin + 7 * $Middle);
        $ipData1 = fread($fd, 4);
        if(strlen($ipData1) < 4) {
            fclose($fd);
            return 'System Error';
        }
        //提取出来的数据转换成长整形,如果数据是负数则加上2的32次幂
        $ip1num = implode('', unpack('L', $ipData1));
        if($ip1num < 0) $ip1num += pow(2, 32);
        //提取的长整型数大于我们IP地址则修改结束位置进行下一次循环
        if($ip1num > $ipNum) {
            $EndNum = $Middle;
            continue;
        }
        //取完上一个索引后取下一个索引
        $DataSeek = fread($fd, 3);
        if(strlen($DataSeek) < 3) {
            fclose($fd);
            return 'System Error';
        }
        $DataSeek = implode('', unpack('L', $DataSeek.chr(0)));
        fseek($fd, $DataSeek);
        $ipData2 = fread($fd, 4);
        if(strlen($ipData2) < 4) {
            fclose($fd);
            return 'System Error';
        }
        $ip2num = implode('', unpack('L', $ipData2));
        if($ip2num < 0) $ip2num += pow(2, 32);
        //没找到提示未知
        if($ip2num < $ipNum) {
            if($Middle == $BeginNum) {
                fclose($fd);
                return 'Unknown';
            }
            $BeginNum = $Middle;
        }
    }
    $ipFlag = fread($fd, 1);
    if($ipFlag == chr(1)) {
        $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)) {
        $AddrSeek = fread($fd, 3);
        if(strlen($AddrSeek) < 3) {
            fclose($fd);
            return 'System Error';
        }
        $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;
        $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;
        }
    }
    fclose($fd);
    //最后做相应的替换操作后返回结果
    if(preg_match('/http/i', $ipAddr2)) {
        $ipAddr2 = '';
    }
    $ipaddr = "$ipAddr1 $ipAddr2";
    $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 = iconv('gbk', 'utf-8//IGNORE', $ipaddr); //转换编码,如果网页的gbk可以删除此行
    return $ipaddr;
}

2. 利用门户网站的接口
目前已知的有腾讯、新浪、网易、搜狐和Google提供IP地址查询API,但是找得到的只有腾讯、新浪和网易的,Google的貌似要用 Google Maps所以没有研究。看了下国内的几个腾讯提供的是JavaScript的,网易提供的是XML,而新浪的有多种格式可以用,注意非XML的数据源都是 GBK格式的,不管是JavaScript调用还是PHP调用都要转换一下编码,不然得到的是乱码。而更需要注意的是,如果一次性查询多个IP,使用门户 网站的API来查询会非常缓慢,我大概写了个for循环试了下,不管是用PHP解析XML还是file_get_contents()函数获取内容,查询 10次以上会变得非常缓慢,甚至可能超时。

腾讯的IP地址API接口地址:http://fw.qq.com/ipaddress,返回的是数据格式为:var IPData = new Array("123.124.2.85","","北京市","");,一个JavaScript的对象,目前还不知道如何输入IP查询。
新浪的IP地址查询接口:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js
新浪多地域测试方法:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js&ip=123.124.2.85
网易有道的IP地址查询接口:http://www.youdao.com/smartresult-xml/search.s?type=ip&q=123.124.2.85
//腾讯API的PHP调用方法

function getIpPlace(){
 $ip=file_get_contents("http://fw.qq.com/ipaddress");
 $ip=str_replace('"',' ',$ip);
 $ip2=explode("(",$ip);
 $a=substr($ip2[1],0,-2);
 $b=explode(",",$a);
 return $b;
}
$ip=getIpPlace();
print_r($ip);

//有道API的PHP调用方法
$url = "http:www.youdao.com/smartresult-xml/search.s?type=ip&q=".$ip;
$doc = new DOMDocument();
   $doc->load($url);
   $smartresult = $doc->getElementsByTagName("product");
   foreach($smartresult as $product)
   {
      $locations = $product->getElementsByTagName("location");
      $location = $locations->item(0)->nodeValue;
   }
   if($location != "")
   {
       echo $i.".".$ip;
       echo "  来自".$location."的网友";
   }
   else
   {
       echo $i.".".$ip;
       echo "  来自火星的网友";
   }
public function sinaIPApi($ip){
   $str = file_get_contents("http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=".$ip);
   $str = iconv("gbk", "utf-8//IGNORE", $str);
   preg_match_all("/[/x{4e00}-/x{9fa5}]+/u",$str,$get);
   $add = implode('',$get[0]);
   return $add;
}
//$get是一个非常棒的二维数组

其中有道和新浪的是我自己写的,新浪API也可以像腾讯API那样用file_get_contents()函数获取完地址后使用一连串的字符串函 数处理,我写的函数使用正则表达式从新浪的返回结果中提供包含中文的字符串,并且分段存入一个二维数组,这个可能只是针对新浪的API有用并且存在 bug。举个例子查询学校分配给我的IP地址后var_dump()一下函数里面的$get变量得到以下结果: array(1) { [0]=> array(6) { [0]=> string(6) "中国" [1]=> string(6) "北京" [2]=> string(6) "北京" [3]=> string(9) "教育网" [4]=> string(6) "学校" [5]=> string(18) "中国地质大学" } },而函数输出的结果则是“中国北京北京教育网学校中国地质大学”,希望我的思路和方法能对别人有用。

最后再次提醒,如果是WordPress请使用第一种方法,否则使用API同时查询所有留言者的真实地址会让PHP超时的,希望各路大牛有更好的方 法,至于限制显示和显示方式等神马的都是WordPress应用问题,同时对于Java和C#来说思路也是一样的,这些后续的问题等我考完试再细说。

PHP 相关文章推荐
PHP如何透过ODBC来存取数据库
Oct 09 PHP
php download.php实现代码 跳转到下载文件(response.redirect)
Aug 26 PHP
PHP中的integer类型使用分析
Jul 27 PHP
纯php打造的tab选项卡效果代码(不用js)
Dec 29 PHP
实现在同一方法中获取当前方法中新赋值的session值解决方法
Jun 26 PHP
php 无限级分类,超级简单的无限级分类,支持输出树状图
Jun 29 PHP
php实现字符串翻转的方法
Mar 27 PHP
php替换字符串中间字符为省略号的方法
May 04 PHP
php安装ssh2扩展的方法【Linux平台】
Jul 20 PHP
php实现产品加入购物车功能(1)
Jul 23 PHP
php查询内存信息操作示例
May 09 PHP
Laravel Eloquent ORM 多条件查询的例子
Oct 10 PHP
linux环境apache多端口配置虚拟主机的方法深入介绍
Jun 09 #PHP
探讨PHP删除文件夹的三种方法
Jun 09 #PHP
如何用php获取文件名后缀
Jun 09 #PHP
深入php多态的实现详解
Jun 09 #PHP
深入PHP autoload机制的详解
Jun 09 #PHP
定义php常量的详解
Jun 09 #PHP
基于php冒泡排序算法的深入理解
Jun 09 #PHP
You might like
PHP 数组基本操作方法详解
2016/06/17 PHP
在PHP语言中使用JSON和将json还原成数组的方法
2016/07/19 PHP
php根据用户名和手机号查询是否存在手机号码
2017/02/16 PHP
数据结构之利用PHP实现二分搜索树
2020/10/25 PHP
jQuery 源代码显示控件 (Ajax加载方式).
2009/05/18 Javascript
浏览器窗口加载和大小改变事件示例
2014/02/27 Javascript
node.js的exports、module.exports与ES6的export、export default深入详解
2017/10/26 Javascript
基于Bootstrap实现城市三级联动
2017/11/23 Javascript
vue组件传递对象中实现单向绑定的示例
2018/02/28 Javascript
Vue2.0 实现单选互斥的方法
2018/04/13 Javascript
Vue路由切换时的左滑和右滑效果示例
2018/05/29 Javascript
vue封装一个简单的div框选时间的组件的方法
2019/01/06 Javascript
使用weixin-java-miniapp配置进行单个小程序的配置详解
2019/03/29 Javascript
vue+elementUI实现表单和图片上传及验证功能示例
2019/05/14 Javascript
JavaScript函数式编程(Functional Programming)声明式与命令式实例分析
2019/05/21 Javascript
基于Nuxt.js项目的服务端性能优化与错误检测(容错处理)
2019/10/23 Javascript
5分钟快速看懂ES6中的反射与代理
2019/12/19 Javascript
js瀑布流布局的实现
2020/06/28 Javascript
Python入门篇之编程习惯与特点
2014/10/17 Python
简单介绍Python中的floor()方法
2015/05/15 Python
Python yield 使用浅析
2015/05/28 Python
python字典的常用操作方法小结
2016/05/16 Python
总结网络IO模型与select模型的Python实例讲解
2016/06/27 Python
微信跳一跳自动运行python脚本
2018/01/08 Python
python3+PyQt5重新实现自定义数据拖放处理
2018/04/19 Python
PyCharm使用Docker镜像搭建Python开发环境
2019/12/26 Python
从零实现一个自定义html5播放器的示例代码
2017/08/01 HTML / CSS
马来西亚太阳镜、眼镜和隐形眼镜网上商店:Focus Point
2018/12/13 全球购物
澳大利亚实惠时尚女装商店:Katies
2019/06/16 全球购物
波兰最早的运动鞋精品店之一:Street Supply
2019/08/29 全球购物
大学应届毕业生个人求职信
2013/09/23 职场文书
大学生活学习的自我评价
2013/12/03 职场文书
学校七一活动方案
2014/01/19 职场文书
《大江保卫战》教学反思
2014/04/11 职场文书
2014小学二年级班主任工作总结
2014/12/05 职场文书
个人职业生涯规划之自我评估篇
2019/09/03 职场文书