纯真ip库(qqwry.dat)转ip2region.xdb
纯真ip库(qqwry.dat)转ip2region.xdb
chfeizy
chfeizy
关注
3 人赞同了该文章
参考 纯真IP库+ip2region,把IP地址转换成省市县编码 - WorrenWang's Blog这个文章的步骤做了优化和调整
1、获取最新的qqwry.dat
可以从https://github.com/metowolf/qqwry.dat/releases 这里下载最新版
2、使用c++把 qqwry.dat转成文本
原文章有问题不能把内容全部提取完,调整为
#include <stdio.h>
#define DAT_FILE "路径/qqwry.dat"
#define OUT_FILE "./out.txt"
FILE *out = NULL;
void showIp(unsigned int ip)
{
char buffer[20] = {0};
sprintf(buffer, "%u.%u.%u.%u ", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
fputs(buffer, out);
}
void getString(FILE *fp)
{
char temp[128] = {0};
int index = 0;
while ((temp[index] = fgetc(fp)) != 0)
{
index++;
}
fputs(temp, out);
}
// 重构 getInfo() 函数(非递归版本)
void getInfo(FILE *fp) {
unsigned char mod;
long offset;
long preOffset;
int count = 0; // 记录已读取的字符串数量
while (count < 2) {
mod = fgetc(fp);
if (mod == 0x01 || mod == 0x02) {
offset = 0;
fread(&offset, 3, 1, fp);
preOffset = ftell(fp);
fseek(fp, offset, SEEK_SET);
} else {
fseek(fp, -1, SEEK_CUR); // 回退非重定向字节
getString(fp); // 读取字符串
count++;
if (count == 1 && mod != 0x01 && mod != 0x02) {
// 若第一个字符串后无重定向,继续读第二个
getString(fp);
count++;
}
}
if (mod == 0x02 && count == 1) {
// 模式2且只读了一个字符串时,返回原位置继续
fseek(fp, preOffset, SEEK_SET);
}
}
}
void parseRecord(FILE *fp, long int recordOffset)
{
unsigned int endIp = 0;
fseek(fp, recordOffset, SEEK_SET); //seek到数据区
fread(&endIp, 4, 1, fp); //读取结束IP
showIp(endIp); //显示结束IP
getInfo(fp); //获取信息
fputs("\n", out);
}
void parseDat(FILE *fp)
{
long int startIndexOffset = 0;
long int endIndexOffset = 0;
long int indexOffset = 0;
long int recordOffset = 0;
unsigned int startIp = 0;
fseek(fp, 0L, SEEK_SET); //seek文件头
fread(&startIndexOffset, 4, 1, fp); //读索引开始偏移量
fread(&endIndexOffset, 4, 1, fp); //读索引结束偏移量
indexOffset = startIndexOffset;
// 修改索引区遍历逻辑
while (indexOffset < endIndexOffset) // 使用 < 而不是 <=
{
fseek(fp, indexOffset, SEEK_SET);
fread(&startIp, 4, 1, fp);
recordOffset = 0; // 关键:清空高位字节
fread(&recordOffset, 3, 1, fp);
indexOffset = ftell(fp);
showIp(startIp);
parseRecord(fp, recordOffset);
}
}
int main(void)
{
FILE *fp = NULL;
out = fopen(OUT_FILE, "wb");
fp = fopen(DAT_FILE, "rb");
if (fp == NULL )
{
printf("open file error!\n");
return 0;
}
parseDat(fp);
fclose(fp);
return 0;
}
3、去高德地图获取省市区json
行政区域查询-基础 API 文档-开发指南-Web服务 API
接口地址
https://restapi.amap.com/v3/config/district
只保留国家下面的省市区
保存好region.json
4、使用python把输出的out.txt 和区域json转成ip2region.txt
原文章去掉了国外,我这里做了保留,并做了调整,可根据自己的需求做调整,调整了out.txt读取编码
不需要区域代码也可以不用region.json,可根据自己的需要调整代码
ip2region.txt的文本格式需要是:ip|ip|国家|区域|省份|城市|ISP
import json,re
# 加载省市县数据
with open("region.json", 'r', encoding='utf-8') as file:
data = json.load(file)
print("---省市县加载完成---")
with open('ip2region.txt', 'w', encoding='utf-8') as outfile:
with open('out.txt', 'r', encoding='gbk') as file:
line = file.readline()
while line:
line2 = line.split(" ")
area = line2[2].replace("\n","").replace("CZ88.NET","").split("–")
containProvince = None
containCity = None
containCounty = None
# 匹配省
for province in data:
if(len(area)>1):
if re.search(province["name"], area[1]) is not None:
# 找到了省
containProvince = province
# 有第二级地区
if(len(area)>2):
for city in province["districts"]:
if re.search(city["name"], area[2]) is not None:
# 找到了省
containCity = city
if(len(area)>3):
for county in city["districts"]:
if re.search(county["name"], area[3]) is not None:
# 找到了省
containCounty = county
if containProvince is None:
#没有省的数据 跳过
#continue
l = len(area)
if l==1:
result=line2[0]+"|"+line2[1]+"|"+area[0]+"|0|0|0|0"
elif l==2:
result=line2[0]+"|"+line2[1]+"|"+area[0]+"|0|"+area[1]+"|0|0"
elif l==3:
result=line2[0]+"|"+line2[1]+"|"+area[0]+"|0|"+area[1]+"|"+area[2]+"|0"
elif l>=4:
result=line2[0]+"|"+line2[1]+"|"+area[0]+"|0|"+area[1]+"|"+area[2]+"|"+area[l-1]
else:
result=line2[0]+"|"+line2[1]+"|"+area[0]+"|"+str(containProvince["adcode"])[:2]+"|"
# if containCity is not None:
# result = result +"|"+containCity["name"]+"|"+containCity["adcode"]
# if containCounty is not None:
# result = result +"|"+containCounty["name"]+"|"+containCounty["adcode"]
l = len(area)
if l==2:
result=result+area[1]+"|0|0"
elif l==3:
result=result+area[1]+"|"+area[2]+"|0"
elif l>=4:
result=result+area[1]+"|"+area[2]+"|"+area[l-1]
# result.replace("\n","")
result=result+"\n"
outfile.write(result)
line = file.readline()
执行命令 python ipdo.py
ip2region.txt生成有124M
5、把ip2region.txt转成ip2region.xdb
使用https://gitee.com/lionsoul/ip2region/tree/master/maker/python 这个实现,也按照原文章把make.py155-161行,上一行的结束IP和下一行的起始IP不连续,直接注释掉这段代码,main.py改名为genxdb.py
执行命令python genxdb.py gen --src=./ip2region.txt --dst=./ip2region.xdb
生成ip2region.xdb 大小有22.8M
py项目结构如图
py项目结构
6、项目里直接使用
java为例:
ip2region.external=false
ip2region.location=classpath:ip2region/ip2region.xdb
logging.level.com.github.hiwepy.ip2region.spring.boot.ext=warn
<dependency>
<groupId>com.github.hiwepy</groupId>
<artifactId>ip2region-spring-boot-starter</artifactId>
<version>2.7.x.20240823.RELEASE</version>
</dependency>
@Autowired
private IP2regionTemplate ip2regionTemplate;
String region=ip2regionTemplate.getRegion(ip);
最后结果类似 中国|33|浙江|宁波Apple|0,经测试可用
最后修改:2025 年 09 月 12 日 08 : 19 PM
© 允许规范转载