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