利用纯真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.wslogs.cn/?wensha_ip=index









