読者です 読者をやめる 読者になる 読者になる

sonickun.log

備忘録

defaultdictを使ったドメイン解析 [Python]

 Pythonのcollectionsモジュールに含まれるdefaultdictを用いて大量のドメインを分類する方法について説明します。
 
 マニュアルはこちら↓
 8.3. collections — 高性能なコンテナ・データ型 — Python 2.7ja1 documentation

 defaultdictはディクショナリ状のオブジェクトを返します。ディクショナリとは定義域(key)と値域(value)という2つの集合上で定義された対応を実現するデータ構造です。今回はドメインをピリオド(.)で区切って、ディクショナリを連結して木構造のようにします。
 
 例えば、ドメインを"www.google.com"とすると、"www", "google", "com"に区切って、後ろの単語を親ノードとして木構造を形成します。つまり、トップレベルドメインが最上位のノードとなるわけです。

 サンプルデータを以下のような通信事業者のドメインのリストとします。(こちらより拝借)

サンプルデータ
docomo.ne.jp
mopera.net
softbank.ne.jp
vodafone.ne.jp
disney.ne.jp
i.softbank.jp
ezweb.ne.jp
biz.ezweb.ne.jp
augps.ezweb.ne.jp
ido.ne.jp
emobile.ne.jp
emobile-s.ne.jp
pdx.ne.jp
willcom.com
wcm.ne.jp


 これを前述のデータ構造に落とし込みます。Pythonソースコードは以下のとおりです。

ソースコード
import collections
import pprint

def tree():
  return collections.defaultdict(tree)

def dicts(t): 
  return {k: dicts(t[k]) for k in t}
 
def add(t, keys):
  for key in keys:
    t = t[key] 

domain_tree = tree()
F = open("sample.txt","r")
for f1 in F:
  f2 = f1.replace("\r\n","")
  f3 = f2.split('.')
  f3.reverse()
  add(domain_tree,f3)

pprint(dicts(domain_tree))
F.close()

 
 これをイチから実装しようとすると相当なコード量になりそうですが、collectionsを用いてだいぶコンパクトに書けました。
 ちなみに"pprint"をというモジュールを使って出力すると、任意のデータ構造の形式に合わせて改行やインデントをしてくれるのでとても見やすくなります。

実行結果
{'com': {'willcom': {}},
 'jp': {'ne': {'disney': {},
               'docomo': {},
               'emobile': {},
               'emobile-s': {},
               'ezweb': {'augps': {}, 'biz': {}},
               'ido': {},
               'pdx': {},
               'softbank': {},
               'vodafone': {},
               'wcm': {}},
        'softbank': {'i': {}}},
 'net': {'mopera': {}}}

  
 こんなかんじでドメインを分類できました。3つのツリーができていることが分かるかと思います。

 これがどんな時に使えるかというと、大量のドメインの中から悪性のドメインを探したり…ゴニョゴニョ なんてときに応用できたりします。defaultdictはドメイン解析だけなく、いろいろな用途に使えそうです。