2017年2月20日 星期一

[python]用python重寫暗黑執行緒的 IpToCountry

因為臉書上有人分享這篇:

http://blog.darkthread.net/post-2017-02-13-in-memory-ip-to-country-mapping.aspx
用 100 行 C# 打造 IP 所屬國家快速查詢功能

然後有人說,是不是用 python 可以更少行,所以我就來試試會變成怎樣。

總之重寫文章裡的最後成果而已,儘量把文章中提到要防呆的也做進去,不然會不公平。

效能測試我因為不知道怎麼快速的弄出 1024 個隨機 ipv4 的列表,https://www.randomlists.com/ip-addresses 這裡給的看來是 ipv6,所以就偷懶不做。若是有人教我或是給我檔案,我可以按照文章中標準來測效能。我程式裡就隨便測 10 個 ip 的總時間,不然會因為時間太少得到 0.0。

import csv
import bisect

class IPCountryFinder:
    def __init__(self, filename):
        self.ranges = []
        self.IPRanges = []
        unknownCode = "--"
        self.CountryNames = {unknownCode:"Unknown"}
        self.IP2CN = {}
        self.count = 0
        with open(filename,'rb') as f:
            s = csv.reader(f, delimiter=',', quotechar='"')
            lastRangeEnd = 0
            for i in s:
                st = int(i[0])
                ed = int(i[1])
                cn = i[4]
                if lastRangeEnd > 0 and st > lastRangeEnd:
                    self.IP2CN[lastRangeEnd] = unknownCode
                    self.IP2CN[st-1] = unknownCode
                    self.count += 2
                self.IP2CN[st] = cn
                self.IP2CN[ed] = cn
                lastRangeEnd = ed + 1
                self.CountryNames[cn] = i[6]
            self.IPRanges = sorted(self.IP2CN.keys())


    def GetIPAddrDec(self, ipAddr):
        b = map(int,ipAddr.split('.'))
        return reduce(lambda s, x: s*256 + x, b)

    def GetCountryCode(self, ipAddr):
        a = self.GetIPAddrDec(ipAddr)
        idx = bisect.bisect_left(self.IPRanges, a) - 1
        if idx < 0:
            idx = -1
        return self.IP2CN[self.IPRanges[idx]]

    def ConvertCountryCodeToName(self, cnCode):
        if cnCode in self.CountryNames:
            return self.CountryNames[cnCode]
        return cnCode

    def GetCountryName(self, ipAddr):
        return self.ConvertCountryCodeToName(self.GetCountryCode(ipAddr))

def test():
    import time
    a = IPCountryFinder('IpToC.csv')
    m = time.time()
    p=a.GetCountryName('1.2.3.4')
    p=a.GetCountryName('1.2.3.8')
    p=a.GetCountryName('1.2.4.8')
    p=a.GetCountryName('1.4.3.8')
    p=a.GetCountryName('6.2.3.8')
    p=a.GetCountryName('1.2.3.34')
    p=a.GetCountryName('1.2.34.8')
    p=a.GetCountryName('1.34.3.8')
    p=a.GetCountryName('34.2.3.8')
    p=a.GetCountryName('18.2.3.8')
    print time.time() - m


test()

如果很想比有幾行的話,全部程式是 66 行,不含 test 的部份是 47 行。這樣比一定不是很公平,c# 光右邊大括號就很多行了。但是少很多型態宣告是比較不殺腦筋的。

1 則留言: