首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python 訪問純真 IP 數據庫:二分查找高效定位歸屬地

     編輯:更多程式設計知識

純真 IP 數據庫(QQWry.dat)是國內常用的 IP 歸屬地離線庫。本文展示將其文本導出版本轉為內存索引,利用 bisect 二分查找實現微秒級查詢的 Python 腳本。

數據準備

假設已將 QQWry.dat 導出為文本 ipdata.txt,每行格式:


1.0.0.0 1.0.0.255 內蒙古 電信
1.0.1.0 1.0.3.255 福建 電信
...

完整查詢代碼


#!/usr/bin/env python
# -*- coding: utf-8 -*-

from bisect import bisect

_LIST1, _LIST2 = [], []
_INIT = False

# IP 字符串轉 32 位整數
ip2int = lambda ip_str: reduce(lambda a, b: (a << 8) + b, [int(i) for i in ip_str.split('.')])

def _init():
    global _LIST1, _LIST2, _INIT
    if not _INIT:
        with open('ipdata.txt', 'rb') as f:
            for line in f:
                parts = line.split()
                ip1, ip2 = parts[0], parts[1]
                addr = b' '.join(parts[2:]).decode('utf-8', 'ignore')
                _LIST1.append(ip2int(ip1))
                _LIST2.append((ip2int(ip1), ip2int(ip2), addr))
        _INIT = True

def ip_from(ip):
    _init()
    i = ip2int(ip)
    idx = bisect(_LIST1, i)  # 找到第一個大於 i 的起始 IP 索引
    if idx == 0:
        return u'unknown ip address %s' % ip
    # 因為 bisect 返回插入位置,實際區間在 idx-1
    start, end, addr = _LIST2[idx - 1]
    if start <= i <= end:
        return addr
    return u'unknown ip address %s' % ip

if __name__ == '__main__':
    print(ip_from('115.238.54.106'))
    print(ip_from('220.181.29.160'))
    print(ip_from('115.238.54.107'))
    print(ip_from('8.8.8.8'))

核心原理解析

性能與優化

  • 時間複雜度:O(log N) 查詢,N 為 IP 段數(純真約 30 萬段,log2(300k) ≈ 19 次比較)
  • 內存佔用:約 30 萬 × (2×4字節 + 字符串) ≈ 30-50 MB,可接受
  • 加速啟動:用 pickle 序列化 _LIST1, _LIST2 到磁盤,下次直接 load
  • 並發安全:只讀索引,多線程直接共享無需鎖

生產級替代方案

  1. ipip.net / MaxMind GeoIP2:更精準(城市級)、支援 IPv6、官方 Python 庫 geoip2
  2. 直接解析 .dat 二進制:避免文本導出步驟,用 struct.unpack 解析原始格式
  3. Redis 存儲:將 IP 段作為有序集合 ZADD ip_ranges start_ip "start|end|addr",ZRANGEBYSCORE 查詢

注意事項

  • 純真庫更新頻率約每 5 天,需定期下載替換
  • IPv6 不支援,純真僅 IPv4
  • 文本編碼通常為 GBK,讀取時需 .decode('gbk')

二分查找 + 整數 IP 是離線 IP 歸屬地查詢的經典高效方案,適合嵌入式、無網絡環境或高性能要求場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
步驟說明
IP 轉整數a.b.c.d → ((a<<24)|(b<<16)|(c<<8)|d),便於數值比較與二分
索引構建_LIST1 存所有段起始 IP(已排序),_LIST2 存 (start, end, addr) 元組
二分查找bisect(_LIST1, ip_int) 返回插入位置,目標區間在 idx-1
邊界檢查確認 start <= ip <= end,防止落在間隙段
懶加載首次查詢時才讀取文件構建索引,避免模塊導入開銷