GTX690战术核显卡导弹 - IP信息查询
type: channel
link: https://www.nekopara.uk/tag/IP%E4%BF%A1%E6%81%AF%E6%9F%A5%E8%AF%A2/
items: 【开发】NekoIPinfo：基于GO语言的高性能IP查询服务端
items: 调用API实现Typecho博客VisitorLoggerPro插件IP信息更新

【开发】NekoIPinfo：基于GO语言的高性能IP查询服务端
type: item
date: 2026-03-05T22:17:14+08:00
description: 高性能IP查询服务端开发日志之前使用IP信息查询的服务也不少了，观察了不少的接口，大部分返回的数据都是json格式的数据，于是我就在想，我能不能也自己开发一个类似的查询工具玩一下？说干就干，在想清楚了软件的架构设计后，我决定开工。架构设计与编程语言选型在架构设计方面，我打算进行前后端彻底解耦，前端为静态网页，后端为纯API程序。对于前端，我计划利用JS来从API获取信息并更新页面对于后端，就做最纯粹的API服务，根据查询请求响应JSON字符串在亲眼见证过PHP，Python这类动态解释型语言的效率噩梦后，我打算使用GO语言进行开发，理由很简单：GO是编译型语言，运行效率比解释型语言要高得多。GO天生就适合开发后端，抗并发能力强。相比于C语言，GO语言要更适合上手。构建前端不得不说，就目前的情况，Gemini仍然是前端之神，我直接让Gemini根据我的想法设计了一套网页，还是相当不错的：代码如下： 猫娘IP地址查询 🐱 🐾 猫娘IP地址查询 您的 IP 地址为 正在获取喵... 🔍 <input type="text" id="ip-input" placeholder="请输入要查询的 IP 地址喵~" class="w-full bg-white/90 backdrop-blur-sm border-2 border-pink-200 text-pink-700 placeholder-pink-300 px-14 py-4 rounded-full md:rounded-r-none focus:outline-none focus:border-pink-400 focus:bg-white transition-all duration-300" > <button id="search-btn" class="w-full md:w-auto mt-4 md:mt-0 bg-gradient-to-r from-pink-400 to-rose-400 hover:from-pink-500 hover:to-rose-500 text-white font-bold text-lg px-8 py-4 rounded-full md:rounded-l-none md:rounded-r-full shadow-md hover:shadow-pink-300/50 transform hover:-translate-y-0.5 transition-all duration-300 whitespace-nowrap active:scale-95" > 查询喵 🐾 🌍 国家 - 🗺️ 省份 - 🏙️ 城市 - 🏢 ISP 运营商 - 🧭 经纬度 - 💡 开发者 API 说明 🛠️ API 调用说明 × 您可以通过发送 GET 请求到我们的接口来获取 IP 详细信息喵： GET https://api.maoniang.example/v1/ip?address={ip} 📦 返回数据格式示例 (JSON): { "code": 200, "msg": "success", "data": { "country": "美国", "province": "加利福尼亚州", "city": "圣克拉拉", "isp": "阿里云", "latitude": "37.355701", "longitude": "-121.955002" } } 我知道了喵 🐾 但是仔细观察，你会发现使用了实时构建的 Tailwind CSS ，其实这样做对于一个静态页面不太友好，于是我让他按照 Tailwind CSS 的样式，原生帮我写了一版样式。因为处理后代码变多了，我就不在这里放出来了。最后静态文件就三个：[chocola@Neko-X99 static]$ tree . ├── favicon.png ├── index.html └── main.css 1 directory, 3 files后续还有界面的美化，目前确定是这样了：变可爱了，有没有？后端设计因为项目的定位是轻量，所以我不打算依赖外部的数据库。再加上IP查询这个业务场景几乎全是读操作，所以使用SQLite是没问题的，读取这方面没有瓶颈。为了实现高效的 IP 查询，数据库表结构的设计至关重要。核心思路是将文本格式的 IP 地址转换为无符号 32 位整数（uint32），这样不仅运算效率高，也便于进行范围比较。数据库表（例如 ip_info）主要包含三个字段：network_start (INTEGER): 存储该 IP 段的起始地址，以 uint32 格式保存。这是查询的核心，我们会根据这个字段建立索引（CREATE INDEX idx_network_sta…

调用API实现Typecho博客VisitorLoggerPro插件IP信息更新
type: item
date: 2025-12-17T23:30:06+08:00
description: 利用B站IP查询API更新访客记录IP地理信息偶然的一次机会，在群里面聊天的时候，群友指出纯真数据库的数据并不准确，并且说有一个可以免费调用的IP地理信息库——B站的IP查询API。据说还是用的ipip.net的企业版，这下得狠狠白嫖了。前置任务：修复VisitorLoggerPro插件BUG如果使用过这个插件一段时间，你就应该发现了，在国家统计图表这项，非常的混乱，似乎是只要记录的字符不一致，就会被当作不同的国家处理，例如美国加利福尼亚洛杉矶和美国纽约就被当作不同的国家列在上面。经过深挖代码斗智斗勇，我发现这个问题我无法从根本解决，因为整个代码太奇怪了，而且用的本地IP数据库也都是乱七八糟的，根本没有做好国家的细分地区分类，一股脑返回，导致所有的信息被集中在一起写进了国家字段。于是，我放弃治疗，直接在后面的调用代码中，增加分类逻辑进行分类：这是原来的代码，单独处理和提取了国内省份： foreach ($countryCountsResult as $row) { $countryName = $row['country'] ?: '未知'; $count = $row['count']; if (!isset($countryData[$countryName])) { $countryData[$countryName] = 0; $totalCountries++; } $countryData[$countryName] += $count; if (strpos($countryName, '中国') !== false) { foreach ($provinces as $province) { if (strpos($countryName, $province) !== false) { if (!isset($provinceData[$province])) { $provinceData[$province] = 0; } $provinceData[$province] += $count; break; } } } }我决定在返回国家信息的时候，进行提取合并并返回： foreach ($countryCountsResult as $row) { $rawCountry = $row['country'] ?? ''; $count = (int)($row['count'] ?? 0); if ($rawCountry === '') { $stdCountry = '未知'; } elseif (strpos($rawCountry, '中国') !== false) { // 所有含“中国”的都归为“中国” $stdCountry = '中国'; // 同时尝试提取省份 foreach ($provinces as $province) { if (strpos($rawCountry, $province) !== false) { if (!isset($provinceData[$province])) { $provinceData[$province] = 0; } $provinceData[$province] += $count; break; // 匹配到第一个即停止 } } } else { // 非中国：尝试匹配标准国家名（用于归一化，如“美国加州”→“美国”） $stdCountry = $rawCountry; // 默认保留原值 foreach ($countries as $country) { if (strpos($rawCountry, $country) !== false) { $stdCountry = $country; break; // 匹配到即停止（因列表已按长优先排序） } } } // 累计国家统计 if (!isset($countryData[$stdCountry])) { $countryData[$stdCountry] = 0; $totalCountries++; } $countryData[$stdCountry] += $count; }当然了，需要补上完整的国家地区列表： $countries = [ // 长名称国家（必须放前面） '波斯尼亚和黑塞哥维那', '赤道几内亚', '圣文森特和格林纳丁斯', '特立尼达和多巴哥', '圣多美和普林西比', '阿拉伯联合酋长国', '沙特阿拉伯', '巴布亚新几内亚', '所罗门群岛', '马绍尔群岛', '密克罗尼西亚联邦', '帕劳共和国', '瓦努阿图共和国', '基里巴斯共和国', '瑙鲁共和国', '图瓦卢', '美属萨摩亚', '英属维尔京群岛', '美属维尔京群…