个性化阅读
专注于IT技术分析

如何使用Symfony 3中的免费GeoLite数据库从访问者的IP中检测城市,国家和地区

点击下载

本文概述

尽管有许多开发人员说应通过浏览器完成地理位置定位, 但是在许多情况下, 你无法使用浏览器的地理位置API。无论你出于何种原因, 都可以通过获取有关用户IP的最重要信息(如国家, 城市和语言)来解决此问题。但是, 从何处以及如何仅从IP检索此类信息?有许多第三方服务, 其中一些是付费服务, 使你可以使用REST API获得此类信息。你将用户的IP作为参数发送, 你将检索有关它的信息。但是, 有一个重要因素会阻止许多开发人员使用此类API, 其中最重要的一点是要记住, 每次用户访问该网站时, 都会向REST API执行请求以获取地理位置信息。出于理智的头脑, 这通常是一种不必要的方法。

这就是为什么还有其他选择的原因, 例如基于数据库的选择。 MaxMind提供的GeoLite2数据库是该方法最知名的免费替代方法之一。在本文中, 我们将向你展示如何使用Symfony项目中的GeoLite2数据库从访问者的IP中获取国家, 城市和地区。

与使用其余API相比, 此方法的优势

在你的服务器中本地拥有一个地理数据库的最明智的原因是, 你无需执行对任何第三方服务器的请求。相反, 你正在查询一个本地数据库, 该数据库的性能仅取决于你的服务器, 而不取决于连接或第三方服务的可用性。你附近有一个数据库来查询有关用户ip的信息, 这种情况再清楚不过了。

1.下载GeoLite2免费数据库

第一步, 你将需要项目中或在系统级别可访问的GeoLite二进制数据库。你可以从此处免费从官方的MaxMind GeoLite2下载数据库:

GeoLite数据库

在本教程中, 我们将使用数据库并将其包含在我们自己的项目中的symfony项目的/ private目录中(请注意, 该目录不存在, 因此需要创建, 你可以根据以下内容更改数据库的路径:你的需求), 我们将使用数据库的城市版本, 该数据库使我们能够获取本文中提到的信息, 尤其是访问者IP的城市, 国家和地区。数据库是使用tar压缩的, 因此你可以使用以下命令从命令行提取其内容:

tar -xzf GeoLite2-City_20180206.tar.gz

另外, 在Windows等其他操作系统中, 你也可以使用7Zip, Winrar或其他解压缩工具来提取其内容。现在你的项目中已有数据库, 目录结构应如下所示:

注意

如果有多个使用数据库的项目, 则可能要将数据库放置在所有项目都可以访问它的其他位置。就像本教程中一样, 我们只需要该项目中的数据库, 我们也将其放置在项目中。

GeoLite2 Symfony项目结构

GeoLite2-City.mmdb文件大小约为63MB, 其中包含从用户IP推断出所需信息所需的所有信息。

2.安装MaxMind GeoIP2 PHP API

为了读取数据库, 你不需要托管在MySQL或其他数据库管理器中。数据库具有来自GeoIP的创建者的特殊格式, 即MaxMind DB。 MaxMind DB文件格式是一种数据库格式, 可以使用有效的二进制搜索树将IPv4和IPv6地址映射到数据记录。二进制数据库分为三个部分:

  1. 二进制搜索树。树的每个级别对应于IPv6地址的128位表示形式中的单个位。
  2. 数据部分。这些是针对特定IP地址返回给客户端的值, 例如”美国”, “纽约”或由多个字段组成的更复杂的地图类型。
  3. 数据库元数据。有关数据库本身的信息。

有关此项目使用的数据库类型的更多信息, 请在此处阅读有关MaxMind DB的更多信息。

现在, 我们需要一种用于这种数据库格式的解析器。幸运的是, MaxMind团队为PHP编写了一个很棒的库, 它使与数据库的交互变得非常容易, 并且你仅需几行代码就可以检索有关用户IP的地理信息。我们正在谈论的是MaxMind GeoIP PHP Api, 该软件包为GeoIP2 Web服务和数据库提供了API。该API还可以与免费的GeoLite2数据库(我们正在使用的数据库)一起使用。你可以使用运行以下命令的composer将此软件包安装在Symfony项目中:

composer require geoip2/geoip2

有关此库的更多信息, 请访问Github上的官方存储库。安装软件包后, 你将可以在Symfony的控制器上使用其类。

3.从控制器检索有关用户IP的地理信息

现在你已经有了数据库和包装器, 可以从中读取信息, 你可以简单地继续从数据库中请求有关指定IP的信息。实现此目的的逻辑如下, 声明一个变量, 该变量包含GeoLite2-City数据库的绝对(本地)路径。这需要作为GeoIp2的Reader类的新实例的第一个参数提供, 你将使用该实例来查询有关IP的信息。最后, 从创建的实例中执行city方法, 该实例提供你要从中获取信息的IP作为第一个参数。显然, 你需要提供一个有效的IP, 否则读取器将引发异常, 显然, 你还需要做好准备, 以使数据库无法识别用户的IP, 因此在查询IP信息时需要捕获AddressNotFoundException。 :

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

// Include GeoIp2 Classes
use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        // Declare the path to the GeoLite2-City.mmdb file (database)
        $GeoLiteDatabasePath = $this->get('kernel')->getRootDir() . '/../private/geolite2-city/GeoLite2-City.mmdb';
        
        // Create an instance of the Reader of GeoIp2 and provide as first argument
        // the path to the database file
        $reader = new Reader($GeoLiteDatabasePath);
        
        try{
            // if you are in the production environment you can retrieve the
            // user's IP with $request->getClientIp()
            // Note that in a development environment 127.0.0.1 will
            // throw the AddressNotFoundException
      

            // In this example, use a fixed IP address in Minnesota
            $record = $reader->city('128.101.101.101');
            
        } catch (AddressNotFoundException $ex) {
            // Couldn't retrieve geo information from the given IP
            return new Response("It wasn't possible to retrieve information about the providen IP");
        }
        
        return new JsonResponse($record);
    }
}

在具有给定IP的示例中, 你将在浏览器中获得以下输出的JSON响应:

{
	"city": {
		"geoname_id": 5037649, "names": {
			"de": "Minneapolis", "en": "Minneapolis", "es": "Mineápolis", "fr": "Minneapolis", "ja": "ミネアポリス", "pt-BR": "Minneapolis", "ru": "Миннеаполис", "zh-CN": "明尼阿波利斯"
		}
	}, "continent": {
		"code": "NA", "geoname_id": 6255149, "names": {
			"de": "Nordamerika", "en": "North America", "es": "Norteamérica", "fr": "Amérique du Nord", "ja": "北アメリカ", "pt-BR": "América do Norte", "ru": "Северная Америка", "zh-CN": "北美洲"
		}
	}, "country": {
		"geoname_id": 6252001, "iso_code": "US", "names": {
			"de": "USA", "en": "United States", "es": "Estados Unidos", "fr": "États-Unis", "ja": "アメリカ合衆国", "pt-BR": "Estados Unidos", "ru": "США", "zh-CN": "美国"
		}
	}, "location": {
		"accuracy_radius": 5, "latitude": 44.9759, "longitude": -93.2166, "metro_code": 613, "time_zone": "America/Chicago"
	}, "postal": {
		"code": "55414"
	}, "registered_country": {
		"geoname_id": 6252001, "iso_code": "US", "names": {
			"de": "USA", "en": "United States", "es": "Estados Unidos", "fr": "États-Unis", "ja": "アメリカ合衆国", "pt-BR": "Estados Unidos", "ru": "США", "zh-CN": "美国"
		}
	}, "subdivisions": [
		{
			"geoname_id": 5037779, "iso_code": "MN", "names": {
				"en": "Minnesota", "es": "Minnesota", "fr": "Minnesota", "ja": "ミネソタ州", "pt-BR": "Minesota", "ru": "Миннесота", "zh-CN": "明尼苏达州"
			}
		}
	], "traits": {
		"ip_address": "128.101.101.101"
	}
}

请注意, $ record变量是stdClass对象, 因此没有getter方法。你可以从PHP中的对象检索属性, 如下所示:

print($record->country->isoCode . "\n"); // 'US'
print($record->country->name . "\n"); // 'United States'
print($record->country->names['zh-CN'] . "\n"); // '美国'

print($record->mostSpecificSubdivision->name . "\n"); // 'Minnesota'
print($record->mostSpecificSubdivision->isoCode . "\n"); // 'MN'

print($record->city->name . "\n"); // 'Minneapolis'

print($record->postal->code . "\n"); // '55455'

print($record->location->latitude . "\n"); // 44.9733
print($record->location->longitude . "\n"); // -93.2323

MaxMind不提供免费的GeoLite2数据库的官方支持。如果你对GeoLite2数据库或GeoIP2 API有疑问, 请参阅stackoverflow的GeoIP问题和解答。

编码愉快!

赞(0)
未经允许不得转载:srcmini » 如何使用Symfony 3中的免费GeoLite数据库从访问者的IP中检测城市,国家和地区

评论 抢沙发

评论前必须登录!