多虧學(xué)了這個(gè)Python庫(kù),一晚上端掉了一個(gè)傳銷團(tuán)伙...
“這是從他們窩點(diǎn)電腦里導(dǎo)出的一份數(shù)據(jù),你先看看能不能找出什么端倪,我再去找?guī)讉€(gè)人問(wèn)問(wèn)話?!?/p>
王隊(duì)丟給我一個(gè)u盤,拿起飯盒胡亂塞了幾口飯,便拎起帽子快步走出了辦公室。
今晚我們根據(jù)情報(bào),前往一處傳銷窩點(diǎn)實(shí)施了抓捕行動(dòng),帶回來(lái)了十幾個(gè)人。
但現(xiàn)場(chǎng)沒(méi)有發(fā)現(xiàn)什么重要證據(jù),抓到的幾個(gè)人也都緘口不言,現(xiàn)在沒(méi)法知道他們是否還有其他的窩點(diǎn),也不知道他們的上線是誰(shuí)人在哪里,行動(dòng)一時(shí)陷入了僵局。
而此時(shí)我手中的這份文件,可能會(huì)成為破局的關(guān)鍵。
我開(kāi)始觀察起這份人員名單:invite_id字段是不重復(fù)的,應(yīng)該和人名是一對(duì)一的關(guān)系;而invited_id重復(fù)的很多,而且基本都是在invite_id出現(xiàn)過(guò)的數(shù)據(jù)。
所以我們基本可以推斷出,這是一份記錄傳銷組織上下線關(guān)系的名單。數(shù)據(jù)足有數(shù)百條之多,可見(jiàn)這是一個(gè)不小的犯罪組織。
不到一個(gè)小時(shí)的功夫,王隊(duì)回來(lái)了。
“沒(méi)用,還是死鴨子嘴硬?!蓖蹶?duì)一屁股坐在椅子上,看了一眼時(shí)間,已經(jīng)快到十二點(diǎn)了?!澳欠輸?shù)據(jù)看的怎么樣了,有沒(méi)有找到什么線索?”
“這是一個(gè)很大的組織,總成員數(shù)有好幾百,我們現(xiàn)在可能只抓到了冰山一角啊?!?/p>
“你說(shuō)的沒(méi)錯(cuò),但正因如此,我們現(xiàn)在必須抓緊時(shí)間?!蓖蹶?duì)走到我的工位前,“我們現(xiàn)在的任務(wù)是先找到他們的上線,擒賊先擒王。但今晚抓到的這群人都審不出東西來(lái),雖然能確定他們的身份,暫時(shí)也還是確定不了他們之間的關(guān)系啊?!?/p>
“你說(shuō)他們之間的關(guān)系?”我突然想起前段時(shí)間看到的networkx這個(gè)python庫(kù),這次說(shuō)不定能派上用場(chǎng)?!敖唤o我了,給我五分鐘時(shí)間?!?/p>
首先用pandas導(dǎo)入文件中的數(shù)據(jù),并篩選出我們需要的部分:
df = pd.read_excel('./doc/1_evidence.xls')
df = df.loc[:, ['id','name','invite_id','invited_id']]
df.columns = ['id','title','to', 'from']
然后調(diào)用networkx庫(kù),生成層級(jí)關(guān)系圖并導(dǎo)出:
G = nx.from_pandas_edgelist(df, 'from', 'to', create_using=nx.DiGraph())
nt = net.Network('960px', '1280px', directed=True)
nt.from_nx(G)
nt.show('1_evidence.html')
這樣我就得到了這份文件對(duì)應(yīng)的層級(jí)關(guān)系圖,上下線關(guān)系瞬間一目了然了:
一瞬間王隊(duì)面露喜色,但立馬又恢復(fù)了嚴(yán)肅:“你這個(gè)是不是有點(diǎn)華而不實(shí)啊,雖然看著挺直觀,但你能從里面找到誰(shuí)是這個(gè)組織的最上級(jí)嗎?”
這當(dāng)然難不倒我,最上級(jí)也就是圖中網(wǎng)絡(luò)的根節(jié)點(diǎn),必然是沒(méi)有其他的點(diǎn)指向它的,所以我們只要遍歷所有節(jié)點(diǎn),找到入度為0的點(diǎn)就可以了。
# 找到根節(jié)點(diǎn)
top_node = []
for node, degrees in G.in_degree():
if degrees == 0:
top_node.append(node)
print('Big Boss:', top_node)
“Big Boss: [100000]” 屏幕上出現(xiàn)了這樣的輸出?!?00000這個(gè)編號(hào)在表中沒(méi)有對(duì)應(yīng)的人,但100000下只有一個(gè)編號(hào)162385的下線,他應(yīng)該就是這個(gè)組織的頭頭了。”
“不錯(cuò),要的就是這個(gè)!我去讓其他同事找這個(gè)人的信息,你繼續(xù)研究數(shù)據(jù),把和這個(gè)人走的近的人全部找出來(lái)!”
這想必也不是難題,既然根節(jié)點(diǎn)已經(jīng)找到了,我們便可以得到所有節(jié)點(diǎn)各自所在的層數(shù)。
# 設(shè)置所有節(jié)點(diǎn)級(jí)別
l = nx.shortest_path_length(G, 100000)
nx.set_node_attributes(G, l, 'level')
# 計(jì)算每級(jí)人員數(shù)目
data = {}
for node, level in l.items():
if level in data.keys():
data[level].append(node)
else:
data[level] = [node]
for level, nodes in data.items():
print(level, len(nodes))
這個(gè)組織竟然已經(jīng)足足發(fā)展了36層,想想真是讓人直冒冷汗,還好我的同事及時(shí)發(fā)現(xiàn)了。
然后根據(jù)層級(jí)給節(jié)點(diǎn)標(biāo)注顏色,方便觀察:
# 添加顏色
for node in G.nodes:
G.nodes[node]['title'] = str(node)
level = G.nodes[node]['level']
if level == 0:
G.nodes[node]['color'] = 'red'
elif level == 1:
G.nodes[node]['color'] = 'orange'
elif level == 2:
G.nodes[node]['color'] = 'yellow'
可以看到這個(gè)編號(hào)162385的人,自己只有兩個(gè)下線,而這兩個(gè)下線每人都各自另外發(fā)展了幾十個(gè)下線,想想還蠻有意思。
“找到了!”王隊(duì)撞開(kāi)辦公室的門,“那個(gè)人找到了,就在今晚逮捕的這群人里面,還有兩個(gè)他的下線也在里面。他們也都承認(rèn)了,現(xiàn)在正在重點(diǎn)審?!笨磥?lái)實(shí)際情況和我的推測(cè)完全相符,多虧了這份名單。
“你今晚立了件大功啊!”王隊(duì)走過(guò)來(lái)拍了拍我的肩膀?!安贿^(guò)現(xiàn)在還沒(méi)完,根據(jù)他們的口供,文件里的就是他們組織的全部人員信息了。你現(xiàn)在把他們發(fā)展下線最多的幾個(gè)人給我找出來(lái),我們根據(jù)信息,立刻安排定點(diǎn)抓捕行動(dòng)?!?/p>
跟前面的差不多,不過(guò)這次我需要遍歷所有節(jié)點(diǎn)的出度數(shù),然后按倒序排序,取前幾項(xiàng)就可以了。
# 給下線前十的目標(biāo)添加顏色
degrees = G.out_degree()
top_nodes = sorted(degrees, key=lambda x: x[1], reverse=True)[:10]
print(top_nodes)
for node in top_nodes:
G.nodes[node[0]]['color'] = 'green'
然后給目標(biāo)節(jié)點(diǎn)加上顏色,方便觀察,最終得到了這樣的關(guān)系圖:
“干得不錯(cuò),只要再把這幾個(gè)人抓到,就相當(dāng)于切斷了這個(gè)組織的大動(dòng)脈,后面的慢慢收尾就可以了。”王隊(duì)把文件合上,笑著對(duì)我說(shuō)?!皼](méi)想到你還有這本事,真是后生可畏??!”
“今天抓到的那三條大魚,現(xiàn)在審出什么結(jié)果了?”相對(duì)于其他,我還是對(duì)案情本身更感興趣。
“別提了,都快笑死我了。這仨人看見(jiàn)證據(jù)直接慌了神,開(kāi)始互相推卸責(zé)任,老大說(shuō)同伙全是另外兩個(gè)人拉來(lái)的他都沒(méi)參與,另外倆人說(shuō)騙局全是老大策劃的他們就是手下打工的,現(xiàn)在估計(jì)還吵著呢...”






