文章背景图

IP归属地引擎——ip2region 自建 API 已上线

2026-08-02
11
- 字
- 分钟
|

起因很简单:博客侧边栏的天气卡片,明明人在成都,却总是显示"北京"。

一、问题:天气定位为什么老不准?

我的博客(Sky-Luxas 主题)天气卡片用的是 IP 定位 → 城市 → 查天气 的链路。第一步 IP 定位原本依赖第三方接口 pconline.xoku.cn,但这个接口越来越不靠谱:

  • 经常返回反爬 HTML 页面而不是 JSON(document.getElementById('trace')... 那种),前端解析直接失败;
  • 请求超时、被拦截是家常便饭;
  • 一旦失败,代码就降级到兜底默认值——配置里写死的 "北京"。

于是:定位失败 → 降级北京 → 缓存 30 分钟 → 一整天都显示北京。

二、选型:翻遍第三方接口后的结论

我实测了一批主流 IP 定位服务,结果如下:

接口 实测结果 评价
pconline.xoku.cn(原用) 返回反爬 HTML ❌ 不可靠
ip-api.com 稳定、字段全、有经纬度 ✅ 好,但免费版不支持 HTTPS,且限频
ipinfo.io 稳定 ✅ 但免费无鉴权限流
whois.pconline.com.cn 返回 GBK 编码 JSON ⚠️ 要转码,麻烦
某淘宝 IP 库翻版 HTTPS 证书过期,只能走 HTTP ❌ 国外只有国家/大洲粒度

结论:第三方在线接口要么不稳定、要么有限流、要么国外定位粗糙。不如自建——把 IP 库放在自己服务器上,毫秒级响应、不限流、不出网。

三、选型:为什么是 ip2region

ip2region(19k+ star 的老牌开源项目):

  • 完全离线:自带 xdb 数据文件,本地查询,不依赖任何在线 API;
  • 速度快:微秒级查询(官方号称 10 微秒量级);
  • 精度好:国内精确到区县级(中文),国外到国家+州(英文);
  • 多语言:官方提供 Python / Node / Go / Java 等 14 种客户端;
  • 体积小:IPv4 库才 11MB。

完美契合"自建 IP 查询 API"的需求。

四、实现:FastAPI + py-ip2region

服务用 Python 写,核心就几十行:

# app.py (核心部分)
import ip2region.util as util
import ip2region.searcher as xdb

# 启动时把整个 xdb 载入内存,全局复用
SEARCHER_V4 = xdb.new_with_buffer(util.IPv4, util.load_content_from_file("ip2region_v4.xdb"))

@app.get("/api/search")
def search(ip: str = None):
    region = SEARCHER_V4.search(ip)
    parts = region.split("|")
    return {
        "ip": ip,
        "region": region,
        "country": parts[0], "province": parts[1],
        "city": parts[2], "isp": parts[3], "iso_code": parts[4],
    }

要点:

  • 全内存缓存(new_with_buffer):查询完全在内存里做,无磁盘 IO,可安全并发;
  • 自动判别 IPv4 / IPv6;
  • 支持无参数查询(查访问者真实 IP,兼容 X-Forwarded-For 反代头);
  • 附带一个 /healthz 健康检查接口,供 Docker 探活。

五、部署:Docker Compose 一键起

# docker-compose.yml
services:
  ip2region-api:
    build: .
    image: ip2region-api:latest
    container_name: ip2region-api
    restart: unless-stopped
    ports:
      - "8764:8080"
    healthcheck:
      test: ["CMD", "python3", "-c", "import urllib.request;urllib.request.urlopen('http://127.0.0.1:8080/healthz', timeout=3)"]

镜像基于 python:3.11-slim,requirements.txt 固定版本,数据文件打进镜像。服务器上 docker compose up -d --build 一条命令搞定。

六、踩坑:CORS 和缓存

坑 1:CORS(最关键的坑)

部署好后 curl 一切正常,但浏览器里天气还是北京。排查发现:页面在 blog.luxas.top,请求的是 api.luxas.top——跨域!我的服务没配 CORS 头,浏览器直接拦截了 fetch,JS 拿不到结果 → 降级北京。

修复:加 FastAPI 的 CORSMiddleware,白名单只放行自己的站点:

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://blog.luxas.top", "https://luxas.top"],
    allow_methods=["GET", "POST"],
)

坑 2:天气缓存

就算定位修好了,浏览器 localStorage 里还躺着 30 分钟的旧缓存(sky_weather_cache_v13)。改完代码还得清缓存/等过期,不然怎么看都是旧结果。

七、API 用法

服务部署在 https://api.luxas.top/ip2region/,用法如下:

① 查询指定 IP

curl "https://api.luxas.top/ip2region/api/search?ip=110.110.110.110"
{
  "ip": "110.110.110.110",
  "region": "中国|黑龙江省|哈尔滨市|铁通|CN",
  "country": "中国",
  "province": "黑龙江省",
  "city": "哈尔滨市",
  "isp": "铁通",
  "iso_code": "CN"
}

② 查访问者自己的 IP(不带参数)

curl "https://api.luxas.top/ip2region/api/search"

③ 健康检查 / 版本信息

curl "https://api.luxas.top/ip2region/healthz"        # ok
curl "https://api.luxas.top/ip2region/api/version"

④ 浏览器可视化:直接打开 https://api.luxas.top/ip2region/,有简单的查询页面。

八、效果

image-MdKB.png
  • 天气卡片定位终于显示成都了;
  • 顺带一提:排查 CPU 问题时还发现天气卡片有一堆 blur-2xl + animate-pulse 的持续动画在烧 CPU,一并修掉了——主页单核占用直接降下来;
  • 这个 API 后续还能复用到其他场景(评论区归属地展示等),也算给 api.luxas.top 子域开张了。

之前还做过一个网易云音乐在线解析的api:https://netease-api2.luxas.top,但是得时不时去更新cookie,懒得折腾,遂弃之。

以后如果再做点什么API也加到这个api.luxas.top域名吧。

原创

IP归属地引擎——ip2region 自建 API 已上线

本文链接: IP归属地引擎——ip2region 自建 API 已上线

本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

评论交流

文章目录