之前一直好奇那种根据 ip 显示出城市是怎么做的
恰好最近在做网站访问信息的记录大概搜索了下
首先要根据 ip 获取地理信息那么就需要有地方存 ip 和地理信息的数据,那么这个数据在哪里呢?
目前找到 maxmind 提供ip2地理信息数据但需要注册
按要求填完注册后登录进入下载数据页面
GeoLite2 数据库是由 MaxMind 提供的一套免费的 IP 地理位置数据库。这套数据库包含了多种类型的数据,包括 ASN(Autonomous System Number,自治系统号)、City(城市)和 Country(国家)。
- ASN 数据:ASN 数据库提供了 IP 地址与其所属的自治系统(Autonomous System)之间的映射关系。自治系统是一个在互联网上由一个或多个路由器管理的路由域,它们通常由一个或多个 ISP(Internet Service Provider,互联网服务提供商)管理。ASN 数据库可以帮助你确定 IP 地址的 ISP 所有者。
- City 数据:City 数据库提供了 IP 地址与其所在城市之间的映射关系。这个数据库提供了更详细的地理位置信息,包括城市名称、邮政编码、经纬度坐标等。
- Country 数据:Country 数据库提供了 IP 地址与其所在国家之间的映射关系。这个数据库提供了国家名称和 ISO 国家代码。
.mmdb
是 MaxMind DB 格式的文件扩展名,它是由 MaxMind 开发的一种数据库格式,用于存储 IP 地理位置信息。
提供 csv(GeoLite2 City: CSV Format)
、mmdb(GeoLite2 City)
格式的数据
这里我们下载 mmdb(GeoLite2 City)
数据使用
数据使用
这里使用了 vercel functions 无服务器函数,开发一个接口根据参数来返回解析后的数据
vercel functions
根据 快速开始文档 初始化一个项目
安装 vercel cli pnpm i -g vercel@latest
然后创建一个项目目录 geo-ip2-api
执行 npm init -y
初始化 package.json
,写入如下内容后执行 npm i
下载依赖, maxmind
是解析上边 mmdb
数据的库
json
{
"name": "geo-ip2-api",
"repository": "",
"license": "MIT",
"private": true,
"devDependencies": {
"@types/node": "^17.0.42",
"@vercel/node": "^2.9.6",
"typescript": "^4.7.3"
},
"dependencies": {
"maxmind": "^4.3.19"
}
}
执行 git init
初始化 git
将下载后的数据解压缩放到项目中 data/GeoLite2-City.mmdb
创建 api/query.ts
,写入如下内容
ts
import type { VercelRequest, VercelResponse } from '@vercel/node'
import { readFileSync } from 'fs'
import { join } from 'path';
import maxmind, { CityResponse, Reader } from 'maxmind';
export default async function handler(req: VercelRequest, res: VercelResponse) {
// 优先获取 query 参数,如果获取不到则获取请求头中的 x-forwarded-for 或 x-real-ip
let ipStr = req.query?.ip as string || req.headers['x-forwarded-for'] as string || req.headers['x-real-ip'] as string;
if (!maxmind.validate(ipStr)) {
return res.json({ code: 1, msg: 'ip 地址格式不正确!' })
}
const filePath = join(process.cwd(), 'data', 'GeoLite2-City.mmdb');
const buffer = readFileSync(filePath);
const lookup = new Reader<CityResponse>(buffer);
const ipInfo = lookup.get(ipStr)
if (!ipInfo) {
return res.json({ code: 1, msg: `未获取到 ip(${ipStr}) 信息!` })
}
return res.json({ code: 0, msg: 'success!', ...ipInfo })
}
本地执行 vercel dev
测试代码,首次需要登录有账号根据提示登录即可,项目首次运行需要回答一些问题一路回车就可以了
因为 GeoLite2-City.mmdb
数据比较大上传完会一直卡住,稍等一些时间停止或者新开窗口运行 vercel dev
显示下图则表示本地运行成功,也可能会有一些其他的输出,显示 Ready!
就认为是成功!
然后浏览器访问 http://localhost:3000/api/query?ip=159.226.171.49
可以看到如下输出
可以看到数据包含了查询 ip 的地理信息,市、国家、地理坐标等,有了这些信息我们就可以分析网站的访问的来源信息
当然这也许不准确因为会有代理、修改请求信息等手段可以改变 ip
但这种我也不是很关心,我只是想记录分析下网站的访问数据做统计图表!
开发完成后可以执行 vercel --prod
进行发布
发布中
发布完成
到这里其实已经实现了一个根据 ip 查询地理信息的接口,我们还可以更完善一些比如将数据格式化一下返回
浏览器输入 https://geo-ip2-api.vercel.app/api/query?ip=159.226.171.49&lang=zh-CN
在项目根目录创建 index.html
这个文件在访问 https://geo-ip2-api.vercel.app/
or http://localhost:3000/
时会被默认加载!
总结
本文从maxmind获取 ip 地理信息数据库,使用 vercel functions 开发了一个可以在线调用的根据 ip 获取地理信息的接口服务,唯一的缺点是线上仅支持 https
访问!
写到这里的时候忽然想起来,之前有一个需求点击按钮定位到当前用户所在的地区,当时能想到就是使用浏览器提供的能力但是线上需要 https
我们没有功能只能搁置!
当时如果想起来使用 ip 定位实现起来也非常简单,因为并不需要太高的精度
扩展内容
maxmind 官方也为一些语言提供了客户端
在线服务
除了上边下载数据本地使用的方式外也提供了在线调用的方法 猛击访问
这里需要关注两点
- 访问需要将用户名和密钥使用 Basic access authentication 加密设置 Authorization 进行请求
- 只能通过
https
才可以进行访问否则返回 403 错误
获取 id 、生成 key 地址
数据加密方法 js
js
function basicAuthEncode(username, password) {
// 将用户名和密码拼接,并使用冒号分隔
var credentials = username + ':' + password;
// 使用 btoa 函数将拼接后的字符串转换为 Base64 编码
var encodedCredentials = btoa(credentials);
// 返回编码后的字符串
return encodedCredentials;
}
返回信息和上边是一样的,因为是通过一份数据源
go 调用
免费在线服务
ip-api 每分钟 45 次请求写 demo 基本够用了
ipgeolocation 每月 3w 访问