如何根据 IP 获取位置?

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 现在很多带有社交性质的应用都陆续的添加了显示地区的功能。为什么要公布属地呢?其实主要目的还是为了净化网络空间,减少网暴的或者谣言的出现。那么如何判断用户的地址呢?答:IP。

现在很多带有社交性质的应用都陆续的添加了显示地区的功能。为什么要公布属地呢?

其实主要目的还是为了净化网络空间,减少网暴的或者谣言的出现。

那么如何判断用户的地址呢?

答:IP

不同的IP 都有不同的属地,这些具体的属地信息一般由运营商来负责维护,也有部分组织或者个人收集维护了IP 对应地理位置的数据库供开发者付费或者免费使用(因为维护一个如此庞大的的数据库会耗费比较大的精力,所以大部分产品都是收费的)。

本文演示所用的 ipdb 是 ipip.net 提供的试用版,试用版比起付费版会少很多功能,我们只用来学习使用方式足够了(商用一定要选择付费版本,否则可能会律师函警告⚠️)。

由于 ipip 的 SDK 文档几乎是没有,所以我记录得详细一点 ipdb 文件和代码示例已经上传到文末 github,有需要可以去下载试用。


核心使用


因为我是个切图仔,所以我选择用 nodejs,我们需要需要下载 ipip 对应的nodejs SDK(全部语言 SDK可以去 github 自行查找)。

$ npm install ipip-ipdb
复制代码

然后编写 JS 脚本来解析,github 的文档基本没有,我在测试文件中发现的这几个 API

import { fileURLToPath } from 'node:url'
import { dirname, resolve } from 'node:path'
import ipdb from 'ipip-ipdb'
const __dirname = dirname(fileURLToPath(import.meta.url))
const client = new ipdb.City(resolve(__dirname, '../db/ipipfree.ipdb'))
function parseIP(ip) {
  console.log(client.findMap(ip, 'CN'))
  console.log(client.findInfo(ip, 'CN'))
}
复制代码

ipip-ipdb 最核心的 API 是一个 City 构造函数,可以创建一个实例。该实例有两个方法,findMap 和 findInfo,findMap 会返回国家、省份、城市的一个 Map 结构,findInfo 会返回一个包含多个信息的对象(试用版只能看到国家、省份、城市),内容如下

1682566140(1).png

总体来说原理就是通过他们维护的 ipdb 文件来查询 ip 的地理位置之间的映射关系,因为 IP 会发生变动,所以维护起来比较麻烦,而且试用版的更新频率也不确定,想要正常商用此功能还是要付费订阅比较好。


锦上添花


核心内容到上面就结束了,后面基础内容较多,可以顺便复习一下 koa 的使用

解析 IP 的工作一般是由后端来完成,所以我们需要搭建一个服务端程序。因为只演示这一个小功能,我们就不去使用那些花里胡哨的框架了,就用最简洁的 koa(或者你想用express等其他的都可以),页面渲染也直接使用模板渲染。

安装 koa 用到的中间件

$ npm install koa @koa/router koa-views ejs
复制代码

准备工作完成之后开始上才艺。还是经典的三段式,在根目录下新建 server.js

import Koa from 'koa'
import Router from '@koa/router'
const app = new Koa()
const router = new Router()
app.use(router.routes())
app.listen(4000, () => {
  console.log('IP 查询服务启动: 4000');
})
复制代码

然后编写路由,并通过ejs模板引擎进行渲染

import views from 'koa-views'
import { fileURLToPath } from 'node:url'
import { dirname } from 'node:path'
const __dirname = dirname(fileURLToPath(import.meta.url))
app.use(views(__dirname + '/views', {
  extension: 'ejs'
}))
router.get('/', async (ctx) => {
  await ctx.render('index', {ip: ''}) // 参数用于渲染内容,默认填入空
})
复制代码

模板引擎加载完成下一步开始编写模板,在 views 目录下新建index.ejs 文件并添加如下代码(省略了 style代码),逻辑控制部分的变量都是通过findInfo方法解析 IP 之后填入的数据。

<body>
  <script>
    window.onload = () => {
      // 加载完成时添加事件绑定
      const search = document.getElementById("search");
      search.addEventListener("keydown", (e) => {
        if(e.code === 'Enter') {
          location.search = '?ip=' + search.value
        }
      })
    }
  </script>
  <div class="container">
    <input id="search" class="search" placeholder="请输入 IP 地址" value="<%- ip %>" />
    <% if(locals.msg) { %>
      <p class="error-msg"><%- msg %> </p>
    <% } else if(locals.res) { %>
      <ul>
        <% if(res.countryName) { %>
          <li><%- res.countryName %> </li>
        <% } %>
        <% if(res.regionName) { %>
          <li><%- res.regionName %> </li>
        <% } %>
        <% if(res.cityName) { %>
          <li><%- res.cityName %> </li>
        <% } %>
      </ul>      
    <% } %> 
  </div>
</body>
复制代码

此时我们的渲染还没有传入数据,再回来完善一下这部分逻辑。执行过程大致为:判断 IP 是否传入,没有传入渲染空页面,有 IP 的话进行正则校验,格式错误报错,否则渲染解析后的数据。

router.get('/', async (ctx) => {
  const { ip } = ctx.request.query
  if (!ip) {
    // 没有 ip 渲染空页面
    await ctx.render('index', {ip: ''})
    return
  }
  if (!isIP(ip)) {
    // 不合法 IP 报错
    await ctx.render('index', { ip, msg: 'IP格式错误' })
  } else {
    // 检索省市区
    const res = parseIP(ip)
    // 返回带数据的页面
    await ctx.render('index', {ip, res})
  }
})
复制代码

此时的效果如下,比之前在控制台中执行要舒服一些

1682566170(1).png

再提供一个接口供 ajax 请求使用,直接返回解析的数据即可

router.get('/json', async (ctx) => {
  const { ip } = ctx.request.query
  if (!ip) {
    ctx.status = 400
    ctx.body = errorFormat(40001, 'IP 不能为空')
    return
  }
  if (!isIP(ip)) {
    ctx.status = 400
    ctx.body = errorFormat(40002, 'IP 格式错误')
  } else {
    const res = parseIP(ip)
    ctx.body = responseFormat(res)
  }
})
复制代码

代码已经上传 github,有需要可以自行拉取

相关文章
|
Android开发
IP归属地是你本人所在的位置吗?
IP归属地是你本人所在的位置吗?
多IP情况下如何获取本地的第一个IP及如何调整本地的第一个IP
我分析了业务的代码,OPTIONS中的Via中的用的是采用gethostbyname获取的。这意味着该函数获取的系统的默认的第一个IP。如果操作系统有多个IP,如何设置它们的优先级呢?
多IP情况下如何获取本地的第一个IP及如何调整本地的第一个IP
|
监控 大数据 定位技术
|
Web App开发
位置
位置
170 0
|
前端开发
前端IP位置定位以及访问日志问题解决
前端IP位置定位以及访问日志问题解决