### Finding domain names with Python

There was a hackathon at my University this past weekend and one of the benefits of attending was a free domain, courtesy of Domain.com. Choosing a domain name is hard, and I've owned about half a dozen of them or so. However, I was curious to know if there were any 3 letter DNs that weren't registered. As it turns out, there are only $$\displaystyle\binom{26}{3}=2600$$ combinations, so searching all of them is more than within reach.

This is a quick Python script I whipped up that I'm sure could be drastically expanded upon and improved. It just prints the DNs one by one as it determines that the expiration date is less than the current date (if that's possible...I don't think that's a good method) and if it doesn't have a listing with whois, which seems to be the case the majority of the time, it's printed as well.
#! /usr/bin/env python3.4
# -*- coding: utf-8 -*-

""" Iterate over all 2600 3 letter domain names x3 for .net, .org and .com and
list any to the terminal that have (supposedly) not been registered.
"""

import whois      # domain lookup
import datetime   # compare registration dates
from itertools import combinations
from typing import List

NOW = datetime.datetime.now

def checkRegistration(domain: str ='aperiodicity.com') -> None:
""" Determine if a domain is registered or not """
try:
global query
query = whois.query(domain).expiration_date
if (NOW() > query):
# ? probably shouldn't work
print(query.isoformat(), domain)
except TypeError:
pass
except Exception:
# most seem to come here, but they're usually forwarded to another
# domain
print('Does not have a listing: ' + domain)

def checkDomain(length: int =3, extensions: List[str] \
=['.org', '.net', '.com']) -> None:

for ext in extensions:
if ext[0] != '.': ext = '.' + ext

alphabet = ''.join(chr(ord('a') + i) for i in range(26))
if (length > len(alphabet)):
raise ValueError('function checkDomain expected length argument <=\
26, got ' + str(length))

for domain in combinations(alphabet, length):
domain = ''.join(domain)
for ext in extensions:
# most of the time is spent here
checkRegistration(domain + ext)

if __name__ == '__main__': checkDomain(length=3)

While the script itself is, I think, unimpressive, there are a couple cool things I saw in writing it. For example, creating the alphabet with the one-liner
alphabet = ''.join(chr(ord('a') + i) for i in range(26))

This makes an import string.ascii_lowercase unnecessary, because we've created it inline. However, this method is indeed slower than the import.

Also, it's neat that we may compare datetime.datetime objects, as I've seldom ran into classes that define the special methods __lt__, __gt__, etc., in such a meaningful way.