纯真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
如果觉得我的文章对你有用,请随意赞赏