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 相关文章推荐
4.与数据库的连接
Oct 09 PHP
php Ajax乱码
Apr 09 PHP
用PHP的超级变量$_POST获取HTML表单(HTML Form) 数据
May 07 PHP
php XPath对XML文件查找及修改实现代码
Jul 27 PHP
PHP防止跨域提交表单
Nov 01 PHP
跟我学Laravel之请求(Request)的生命周期
Oct 15 PHP
php定时执行任务设置详解
Feb 06 PHP
PHP大文件分割上传 PHP分片上传
Aug 28 PHP
PHP用函数嵌入网站访问量计数器
Oct 27 PHP
PHP设计模式之模板模式定义与用法详解
Dec 20 PHP
laravel框架路由分组,中间件,命名空间,子域名,路由前缀实例分析
Feb 18 PHP
解决PHPstudy Apache无法启动的问题【亲测有效】
Oct 30 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设计模式  Command(命令模式)
2011/06/17 PHP
PHP中strtr与str_replace函数运行性能简单测试示例
2019/06/22 PHP
Laravel配合jwt使用的方法实例
2020/10/25 PHP
一个高效的JavaScript压缩工具下载集合
2007/03/06 Javascript
JavaScript面向对象(极简主义法minimalist approach)
2012/07/17 Javascript
JavaScript之Getters和Setters 平台支持等详细介绍
2012/12/07 Javascript
node.js中的fs.futimes方法使用说明
2014/12/17 Javascript
Javascript中判断对象是否为空
2015/06/10 Javascript
JavaScript中的return语句简单介绍
2015/12/07 Javascript
JS组件Bootstrap实现弹出框和提示框效果代码
2015/12/08 Javascript
浅谈javascript控制HTML5的全屏操控,浏览器兼容的问题
2016/10/10 Javascript
Angular入口组件(entry component)与声明式组件的区别详解
2018/04/09 Javascript
前后端如何实现登录token拦截校验详解
2018/09/03 Javascript
JS实现图片切换效果
2018/11/17 Javascript
五句话帮你轻松搞定js原型链
2020/12/09 Javascript
[03:28]2014DOTA2国际邀请赛 走近EG战队天才中单Arteezy
2014/07/12 DOTA
python中as用法实例分析
2015/04/30 Python
简单介绍Python中用于求最小值的min()方法
2015/05/15 Python
scrapy爬虫实例分享
2017/12/28 Python
Python实现通讯录功能
2018/02/22 Python
基于Python中求和函数sum的用法详解
2018/06/28 Python
Python smtp邮件发送模块用法教程
2020/06/15 Python
Python collections.deque双边队列原理详解
2020/10/05 Python
一家专门做特卖的网站:唯品会
2016/10/09 全球购物
英国书籍、CD、DVD和游戏的第一道德零售商:Awesome Books
2020/02/22 全球购物
前台接待岗位职责
2013/12/03 职场文书
2014年计算机专业个人自我评价
2014/01/19 职场文书
竞争性谈判邀请书
2014/02/06 职场文书
产假请假条
2014/04/10 职场文书
2014党员干部四风问题对照检查材料思想汇报
2014/09/24 职场文书
领导班子整改措施
2014/10/24 职场文书
小学庆六一主持词
2015/06/30 职场文书
干货:如何写好工作总结报告!
2019/05/10 职场文书
怎样写工作总结啊!
2019/06/18 职场文书
python基于tkinter制作无损音乐下载工具
2021/03/29 Python
python中字符串String及其常见操作指南(方法、函数)
2022/04/06 Python