摘要:下面清單展示了地址所屬地查找程序的具體實現(xiàn)方法將地址轉(zhuǎn)換為分值以便執(zhí)行命令查找唯一城市方法用來根據(jù)指定的分隔符將字符串進行分割。
上一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第2節(jié):計數(shù)器和統(tǒng)計數(shù)據(jù)
下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第4節(jié):服務(wù)的發(fā)現(xiàn)與配置
通過將統(tǒng)計數(shù)據(jù)和日志存儲到Redis里面,我們可以收集訪客在系統(tǒng)中的行為信息。但是直到目前為止,我們都忽略了訪客行為中非常重要的一部分,那就是:這些訪客是從哪里來的?為了回答這個問題,在這一節(jié)中,我們將構(gòu)建一系列用于分析和載入IP所屬地數(shù)據(jù)庫的函數(shù),并編寫一個可以根據(jù)訪客的IP地址來查找訪客所在城市、行政區(qū)、國家的函數(shù)。我們先來看看下面這個例子。
隨著Fake Game公司的游戲越來越受追捧,來自世界各地的玩家也越來越多。盡管像Google Analytics這樣的工具可以讓Fake Game公司知道玩家主要來自哪些國家或地區(qū),但是為了更深入的了解玩家,F(xiàn)ake Game公司還希望自己能夠知道玩家們所在的城市,而我們要做的就是將一個IP所屬城市數(shù)據(jù)庫載入Redis里面,然后通過搜索整個數(shù)據(jù)庫來發(fā)現(xiàn)玩家所在的位置。
我們之所以使用Redis而不是傳統(tǒng)的關(guān)系數(shù)據(jù)庫來實現(xiàn)IP所屬地查找功能,是因為Redis實現(xiàn)的IP所屬地查找程序在運行速度上更具有優(yōu)勢。另一方面,因為對用戶進行定位所需的信息量非常龐大,在應(yīng)用程序啟動時載入這些信息將影響應(yīng)用程序的啟動速度,所以我們也沒有使用本地查找表來實現(xiàn)IP所屬地查找功能。實現(xiàn)IP所屬地查找功能首先要做的就是將一些數(shù)據(jù)表載入Redis里面,接下來的小節(jié)將對這個步驟進行介紹。
載入城市表格為了開發(fā)IP所屬地查找程序,我們將使用一個IP所屬城市數(shù)據(jù)庫作為測試數(shù)據(jù)。這個數(shù)據(jù)庫包含兩個非常重要的文件:
一個是GeoLiteCity-Blocks.csv,它記錄了多個IP地址段以及這些地址段所屬城市的ID
另一個是GeoLiteCity-Location.csv它記錄了城市ID與城市名、地區(qū)名、州縣名以及我們不會用到的其他信息之間的映射。
實現(xiàn)IP所屬地查找程序會用到兩個查找表:
第一個查找表需要根據(jù)輸入的IP地址來查找IP所屬城市的ID
第二個查找表則需要根據(jù)輸入的城市ID來查找ID對應(yīng)城市的實際信息(這個城市信息中還會包括城市所在地區(qū)的其他信息)
根據(jù)IP地址來查找城市ID的查找表由有序集合實現(xiàn),這個有序集合的成員為具體的城市ID,而分值則是一個根據(jù)IP地址計算出來的整數(shù)值。為了創(chuàng)建IP地址與城市ID之間的映射,程序需要將點分十進制格式的IP地址轉(zhuǎn)換為一個整數(shù)分值,下面的ip_to_score()函數(shù)定義了整個轉(zhuǎn)化過程:IP地址中的每8個二進制會被看做是無符號整數(shù)中的1字節(jié),其中IP地址最開頭的8個二進制位最高位。
def ip_ti_score(ip_address): score=0 for v in ip_address.split("."): score=score*256+int(v,10) return score if __name__ == "__main__": y=ip_ti_score("117.61.12.128") print(y) x=117 x=x*256+61 x=x*256+12 x=x*256+128 print(x)
運行結(jié)果:
1966935168 1966935168
在將IP地址轉(zhuǎn)換為整數(shù)分值之后,程序就可以創(chuàng)建IP地址與城市ID之間的映射了。因為多個IP地址范圍可能會被映射到同一個城市ID,所以程序會在普通的城市ID后面,加上一個_字符以及有序集合目前已知的城市ID的數(shù)量,以此來構(gòu)建一個獨一無二的唯一城市ID。下面代碼展示了程序時如何創(chuàng)建IP地址與城市ID之前的映射的。
import csv #這個函數(shù)在執(zhí)行時需要輸入GeoLiteCity-Blocks.csv文件所在的路徑 def import_ips_to_redis(conn,filename): csv_file=csv.reader(open(filename,"rb")) #enumerate() 函數(shù)用于將一個可遍歷的數(shù)據(jù)對象(如列表、元組或字符串)組合為一個索引序列, # 同時列出數(shù)據(jù)和數(shù)據(jù)下標(biāo),一般用在 for 循環(huán)當(dāng)中。 for count,row in enumerate(csv_file): start_ip=row[0] if row else "" #按照需要將IP地址轉(zhuǎn)換為分值 if "i" in start_ip.lower(): continue if "." in start_ip: start_ip=ip_to_score(start_ip) elif start_ip.isdigit(): start_ip=int(start_ip,10) else: #略過文件的第一行以及格式不正確的條目 continue #構(gòu)建唯一的城市ID city_id=row[2]+"_"+str(count) #將城市ID及其對應(yīng)的IP地址分值添加到有序集合里面。 conn_zadd("ip2cityid:",city_id,start_ip)
在調(diào)用import_ips_to_redis()函數(shù)并將所有IP地址都載入Redis之后,我們會像下面代碼展示的那樣,創(chuàng)建一個城市ID映射至城市信息的散列。因為所有城市信息的格式都是固定的,并且不會隨著時間而發(fā)生變化,所有我們會將這些信息編碼為JSON列表然后再進行存儲。
def import_cities_to_redis(conn,filename): for row in csv.reader(open(filename,"rb")): if len(row)<4 or not row[0].isdigit(): continue row=[i.decode("latin-1") for i in row] city_id=row[0] country=row[1] region=row[2] city=row[3] conn.hset("cityid2city:",city_id,json.dumps([city,region,country]))
在將所需的信息全部存儲到Redis里面之后,接下來要考慮的就是如何實現(xiàn)IP地址查找功能了。
查找IP所屬城市為了實現(xiàn)IP地址查找功能,我們在上一個小節(jié)已經(jīng)將代表城市ID所屬IP地址段起始端的整數(shù)分值添加到了有序集合里面。要根據(jù)給定IP地址來查找所屬城市,程序首先會使用ip_to_score()函數(shù)將給定的IP地址轉(zhuǎn)換為分值,然后在所有分值小于或等于給定IP地址里面,找出分值最大的那個IP地址所對應(yīng)的城市ID。這個查找城市ID的操作可以通過調(diào)用zrevrangebyscore命令并將選項start和num的參數(shù)分別設(shè)為0和1來完成。在找到城市ID之后,程序就可以在存儲著城市ID與城市信息映射的散列里面獲取ID對應(yīng)城市的信息了。
下面清單展示了IP地址所屬地查找程序的具體實現(xiàn)方法:;
def find_city_by_ip(conn,ip_address): if isinstance(ip_address,str): #將IP地址轉(zhuǎn)換為分值以便執(zhí)行zrevrangebyscore命令 ip_address=ip_to_score(ip_address) #查找唯一城市ID city_id=conn.zrevrangebyscore("ip2cityed:",ip_address,0,start=0,num=1) if not city_id: return None #partition() 方法用來根據(jù)指定的分隔符將字符串進行分割。 # 如果字符串包含指定的分隔符,則返回一個3元的元組, # 第一個為分隔符左邊的子串,第二個為分隔符本身,第三個為分隔符右邊的子串。 #將唯一城市ID轉(zhuǎn)換為普通城市ID city_id=city_id[0].partition("_")[0] #從散列里面取出城市信息 return json.loads(conn.hget("cityid2city:",city_id))
通過上面函數(shù),我們現(xiàn)在可以基于IP地址來查找相應(yīng)的城市信息并對用戶的來源地進行分析了。
本節(jié)介紹的【將數(shù)據(jù)轉(zhuǎn)換為整數(shù)并搭配有序集合進行操作】的做法非常有用,它可以極大簡化對特定元素或特定范圍的查找工作。
上一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第2節(jié):計數(shù)器和統(tǒng)計數(shù)據(jù)
下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第4節(jié):服務(wù)的發(fā)現(xiàn)與配置
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/42725.html
摘要:在通常情況下,即使只更新配置中的一個標(biāo)志,也會導(dǎo)致更新后的配置文件被強制推送至所有服務(wù)器,收到更新的服務(wù)器可能需要重新載入配置甚至可能還要重啟應(yīng)用程序服務(wù)器。將被包裹函數(shù)的一些有用的元數(shù)據(jù)復(fù)制給配置處理器。 上一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第3節(jié):查找IP所屬城市以及國家 隨著我們越來越多地使用Redis以及其他服務(wù),如何存儲各項服務(wù)的...
摘要:清理程序通過對記錄已知計數(shù)器的有序集合執(zhí)行命令來一個接一個的遍歷所有已知的計數(shù)器。 上一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第1節(jié):使用Redis來記錄日志下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第3節(jié):查找IP所屬城市以及國家 正如第三章所述,通過記錄各個頁面的被訪問次數(shù),我們可以根據(jù)基本的訪問計數(shù)信息...
閱讀 970·2023-04-25 23:55
閱讀 2691·2023-04-25 14:13
閱讀 3286·2019-08-26 13:47
閱讀 2957·2019-08-23 18:16
閱讀 616·2019-08-23 17:20
閱讀 3217·2019-08-23 16:55
閱讀 3135·2019-08-22 15:39
閱讀 3184·2019-08-20 18:10