Module sws.domains
A module for dealing with getting domain information such as:
- If a domain is available
- Who a domain is registered with
- Other domain details such as, creation_date, name_servers etc.
Notes
- If whois is not installed, the package/executable will be installed
Examples
Getting details about an available domain
from sws.domains import get_domain_info, domain_availability
domain_details = get_domain_info('kieranwood.com')
domain_availability(domain_details) # ('Domain available', True)
Getting details of an unavailable domain
from sws.domains import get_domain_info, domain_availability
domain_details = get_domain_info('kieranwood.ca')
domain_availability(domain_details) # ('Domain unavailable until 6/November/2020', False)
Getting details about an unregistered domain
from sws.domains import get_domain_info
print(get_domain_info('kieranwood.com')) # {'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': domain, 'name_servers': False, 'registrant_cc': False, 'registrar': False}
Getting details of a registered domain
from sws.domains import get_domain_info
print(get_domain_info('kieranwood.ca')) # {'creation_date': datetime.datetime(2018, 11, 6, 5, 9, 47), 'expiration_date': datetime.datetime(2020, 11, 6, 5, 9, 47), 'last_updated': datetime.datetime(2020, 1, 8, 8, 9, 44), 'name': 'kieranwood.ca', 'name_servers': {'kevin.ns.cloudflare.com', 'sharon.ns.cloudflare.com'}, 'registrant_cc': 'redacted for privacy', 'registrar': 'Go Daddy Domains Canada, Inc'}
Expand source code
"""A module for dealing with getting domain information such as:
- If a domain is available
- Who a domain is registered with
- Other domain details such as, creation_date, name_servers etc.
Notes
-----
- If whois is not installed, the package/executable will be installed
Examples
--------
### Getting details about an available domain
```
from sws.domains import get_domain_info, domain_availability
domain_details = get_domain_info('kieranwood.com')
domain_availability(domain_details) # ('Domain available', True)
```
### Getting details of an unavailable domain
```
from sws.domains import get_domain_info, domain_availability
domain_details = get_domain_info('kieranwood.ca')
domain_availability(domain_details) # ('Domain unavailable until 6/November/2020', False)
```
### Getting details about an unregistered domain
```
from sws.domains import get_domain_info
print(get_domain_info('kieranwood.com')) # {'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': domain, 'name_servers': False, 'registrant_cc': False, 'registrar': False}
```
### Getting details of a registered domain
```
from sws.domains import get_domain_info
print(get_domain_info('kieranwood.ca')) # {'creation_date': datetime.datetime(2018, 11, 6, 5, 9, 47), 'expiration_date': datetime.datetime(2020, 11, 6, 5, 9, 47), 'last_updated': datetime.datetime(2020, 1, 8, 8, 9, 44), 'name': 'kieranwood.ca', 'name_servers': {'kevin.ns.cloudflare.com', 'sharon.ns.cloudflare.com'}, 'registrant_cc': 'redacted for privacy', 'registrar': 'Go Daddy Domains Canada, Inc'}
```
"""
# Standard Library Dependencies
import os # Used for path manipulation
import sys # Used to exit safely during errors
import logging # Used for logging in debugging etc.
import subprocess # Used to execute existing binaries
from shutil import move # Used to move folders within the os
from datetime import datetime # Used for interpreting dates and times
from calendar import month_name # Used to convert integer month representations to string representations
# Third Party Dependencies
import whois # Used to pull domain information
from pystall.core import build, ZIPResource, _add_to_path, APTResource # Used to install whois binary
def get_domain_info(domain: str) -> dict:
"""Returns a dictionary of all domain information
Parameters
----------
domain : str
The domain you want the details for
Returns
-------
dict
A dictionary of a whois.Domain query
Notes
-----
- If whois is not installed, the package/executable will be installed
- Make sure to use the domain, and not just a url for example https://kieranwood.ca/hello is a url but kieranwood.ca is a domain
- In the case that a protocol (http:// or https://) is provided it will be stripped, be aware this can cause comparison issues to the 'name' parameter of the dictionary
Raises
------
ValueError:
If provided domain is not a valid domain (i.e. Subdomain, or URL instead of domain)
Examples
--------
Getting details about an unregistered domain
```
from sws.domains import get_domain_info
print(get_domain_info('kieranwood.com')) # {'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': domain, 'name_servers': False, 'registrant_cc': False, 'registrar': False}
```
Getting details of a registered domain
```
from sws.domains import get_domain_info
print(get_domain_info('kieranwood.ca')) # {'creation_date': datetime.datetime(2018, 11, 6, 5, 9, 47), 'expiration_date': datetime.datetime(2020, 11, 6, 5, 9, 47), 'last_updated': datetime.datetime(2020, 1, 8, 8, 9, 44), 'name': 'kieranwood.ca', 'name_servers': {'kevin.ns.cloudflare.com', 'sharon.ns.cloudflare.com'}, 'registrant_cc': 'redacted for privacy', 'registrar': 'Go Daddy Domains Canada, Inc'}
```
"""
logging.info(f"Entering get_domain_info(domain={domain})")
_install_whois() # Verify/install whois
# Validation
## Strip protocols
if domain.startswith("https://"):
logging.info(f"Stripping https:// protocol from {domain}")
domain = domain.replace("https://", "")
elif domain.startswith("http://"):
logging.info(f"Stripping http:// protocol from {domain}")
domain = domain.replace("http://", "")
## Raise error if subdomain
logging.info(f"Confirming domain {domain} is not a subdomain")
if len(domain.split(".")) > 2: # If domain is subdomain
raise ValueError(f"Provided domain {domain} is likely a subdomain")
## Raise Error if invalid TLD
try:
logging.info(f"Querying {domain} with whois")
domain_details = whois.query(domain)
except Exception as e:
if "Unknown TLD:" in str(e):
raise ValueError(f"Domain {domain} is not a valid domain")
# ## TODO: When new version of python-whois-extended releases uncomment below code
# try:
# if os.name == "nt" and os.path.exists(os.path.realpath(f"{os.getenv('USERPROFILE')}\\..\\..\\whois")):
# INSTALL_FOLDER = os.path.realpath(f"{os.getenv('USERPROFILE')}\\..\\..\\whois\\whois.exe")
# domain_details = whois.query(domain, executable=INSTALL_FOLDER)
# else:
# domain_details = whois.query(domain)
# except Exception as e:
# if "Unknown TLD:" in str(e):
# raise ValueError(f"Domain {domain} is not a valid domain")
# else:
# raise e
# Parse response
if not domain_details: # If the domain is not registered
logging.info(f"""Exiting get_domain_info() and returning {{'creation_date': False,
'expiration_date': False,
'last_updated': False,
'name': {domain},
'name_servers': False,
'registrant_cc': False,
'registrar': False}}""")
return {'creation_date': False,
'expiration_date': False,
'last_updated': False,
'name': domain,
'name_servers': False,
'registrant_cc': False,
'registrar': False}
elif domain_details is None: # If the domain is not registered and query completely failed
logging.info(f"""Exiting get_domain_info() and returning {{'creation_date': False,
'expiration_date': False,
'last_updated': False,
'name': {domain},
'name_servers': False,
'registrant_cc': False,
'registrar': False}}""")
return {'creation_date': False,
'expiration_date': False,
'last_updated': False,
'name': domain,
'name_servers': False,
'registrant_cc': False,
'registrar': False}
else: # If there was domain info
logging.info(f"Exiting get_domain_info() and returning {vars(domain_details)}")
return vars(domain_details)
def domain_availability(domain_query: dict) -> tuple:
"""Checks the availability of a domain
Parameters
----------
domain_query : dict
The dictionary representation of a whois.Domain object
Returns
-------
tuple[str, bool]
Returns a tuple with a printable string about availability,
and a bool that's True if it's available and False if not
Examples
--------
Getting details about an available domain
```
from sws.domains import get_domain_info, domain_availability
domain_details = get_domain_info('kieranwood.com')
domain_availability(domain_details) # ('Domain available', True)
```
Getting details of an unavailable domain
```
from sws.domains import get_domain_info, domain_availability
domain_details = get_domain_info('kieranwood.ca')
domain_availability(domain_details) # ('Domain unavailable until 6/November/2020', False)
```
"""
logging.info(f"Entering get_domain_info(domain_query={domain_query})")
if not domain_query["expiration_date"] or domain_query["expiration_date"] < datetime.today():
logging.info("Domain available, returning ('Domain available', True)")
return "Domain available", True
else:
logging.info(f"Domain unavailable, returning ('Domain {domain_query['name']} unavailable until {domain_query['expiration_date'].day}/{month_name[domain_query['expiration_date'].month]}/{domain_query['expiration_date'].year}', False)")
return f"Domain {domain_query['name']} unavailable until {domain_query['expiration_date'].day}/{month_name[domain_query['expiration_date'].month]}/{domain_query['expiration_date'].year}", False
def _install_whois():
"""Used to install whois binary if it isn't available"""
logging.info("Entering _install_whois()")
# Setting up default downloads folder based on OS
if os.name == "nt":
DOWNLOAD_FOLDER = f"{os.getenv('USERPROFILE')}\\Downloads"
INSTALL_FOLDER = os.path.realpath(f"{os.getenv('USERPROFILE')}\\..\\..\\whois")
else: # PORT: For *Nix systems
DOWNLOAD_FOLDER = f"{os.getenv('HOME')}/Downloads"
INSTALL_FOLDER = f"{os.getenv('HOME')}/whois"
if not os.path.exists(INSTALL_FOLDER):
try:
subprocess.Popen("whois") # Check if binary is installed
except FileNotFoundError:
if os.name == "nt": # Install windows version of whois
logging.info(f"System is windows manually installing: DOWNLOAD_FOLDER = {DOWNLOAD_FOLDER}, INSTALL_FOLDER = {INSTALL_FOLDER}")
logging.info(f"Downloading whois from https://download.sysinternals.com/files/WhoIs.zip and installing to {INSTALL_FOLDER}")
build(ZIPResource("whois", "https://download.sysinternals.com/files/WhoIs.zip", overwrite_agreement=True))
move(f"{DOWNLOAD_FOLDER}{os.sep}whois", INSTALL_FOLDER)
logging.warning("Beginning adding whois to path variable")
_add_to_path(INSTALL_FOLDER) # TODO: When new version of python-whois-extended releases remove this call
print("Whois has been installed, restart script") # TODO: When new version of python-whois-extended releases remove this call
sys.exit()
else: # Linux Installation
logging.info(f"System is nix, installing with APT: DOWNLOAD_FOLDER = {DOWNLOAD_FOLDER}, INSTALL_FOLDER = {INSTALL_FOLDER}")
build(APTResource("whois", "whois", overwrite_agreement=True))
Functions
def domain_availability(domain_query: dict) ‑> tuple
-
Checks the availability of a domain
Parameters
domain_query
:dict
- The dictionary representation of a whois.Domain object
Returns
tuple[str, bool]
- Returns a tuple with a printable string about availability, and a bool that's True if it's available and False if not
Examples
Getting details about an available domain
from sws.domains import get_domain_info, domain_availability domain_details = get_domain_info('kieranwood.com') domain_availability(domain_details) # ('Domain available', True)
Getting details of an unavailable domain
from sws.domains import get_domain_info, domain_availability domain_details = get_domain_info('kieranwood.ca') domain_availability(domain_details) # ('Domain unavailable until 6/November/2020', False)
Expand source code
def domain_availability(domain_query: dict) -> tuple: """Checks the availability of a domain Parameters ---------- domain_query : dict The dictionary representation of a whois.Domain object Returns ------- tuple[str, bool] Returns a tuple with a printable string about availability, and a bool that's True if it's available and False if not Examples -------- Getting details about an available domain ``` from sws.domains import get_domain_info, domain_availability domain_details = get_domain_info('kieranwood.com') domain_availability(domain_details) # ('Domain available', True) ``` Getting details of an unavailable domain ``` from sws.domains import get_domain_info, domain_availability domain_details = get_domain_info('kieranwood.ca') domain_availability(domain_details) # ('Domain unavailable until 6/November/2020', False) ``` """ logging.info(f"Entering get_domain_info(domain_query={domain_query})") if not domain_query["expiration_date"] or domain_query["expiration_date"] < datetime.today(): logging.info("Domain available, returning ('Domain available', True)") return "Domain available", True else: logging.info(f"Domain unavailable, returning ('Domain {domain_query['name']} unavailable until {domain_query['expiration_date'].day}/{month_name[domain_query['expiration_date'].month]}/{domain_query['expiration_date'].year}', False)") return f"Domain {domain_query['name']} unavailable until {domain_query['expiration_date'].day}/{month_name[domain_query['expiration_date'].month]}/{domain_query['expiration_date'].year}", False
def get_domain_info(domain: str) ‑> dict
-
Returns a dictionary of all domain information
Parameters
domain
:str
- The domain you want the details for
Returns
dict
- A dictionary of a whois.Domain query
Notes
- If whois is not installed, the package/executable will be installed
- Make sure to use the domain, and not just a url for example https://kieranwood.ca/hello is a url but kieranwood.ca is a domain
- In the case that a protocol (http:// or https://) is provided it will be stripped, be aware this can cause comparison issues to the 'name' parameter of the dictionary
Raises
Valueerror
If provided domain is not a valid domain (i.e. Subdomain, or URL instead of domain)
Examples
Getting details about an unregistered domain
from sws.domains import get_domain_info print(get_domain_info('kieranwood.com')) # {'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': domain, 'name_servers': False, 'registrant_cc': False, 'registrar': False}
Getting details of a registered domain
from sws.domains import get_domain_info print(get_domain_info('kieranwood.ca')) # {'creation_date': datetime.datetime(2018, 11, 6, 5, 9, 47), 'expiration_date': datetime.datetime(2020, 11, 6, 5, 9, 47), 'last_updated': datetime.datetime(2020, 1, 8, 8, 9, 44), 'name': 'kieranwood.ca', 'name_servers': {'kevin.ns.cloudflare.com', 'sharon.ns.cloudflare.com'}, 'registrant_cc': 'redacted for privacy', 'registrar': 'Go Daddy Domains Canada, Inc'}
Expand source code
def get_domain_info(domain: str) -> dict: """Returns a dictionary of all domain information Parameters ---------- domain : str The domain you want the details for Returns ------- dict A dictionary of a whois.Domain query Notes ----- - If whois is not installed, the package/executable will be installed - Make sure to use the domain, and not just a url for example https://kieranwood.ca/hello is a url but kieranwood.ca is a domain - In the case that a protocol (http:// or https://) is provided it will be stripped, be aware this can cause comparison issues to the 'name' parameter of the dictionary Raises ------ ValueError: If provided domain is not a valid domain (i.e. Subdomain, or URL instead of domain) Examples -------- Getting details about an unregistered domain ``` from sws.domains import get_domain_info print(get_domain_info('kieranwood.com')) # {'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': domain, 'name_servers': False, 'registrant_cc': False, 'registrar': False} ``` Getting details of a registered domain ``` from sws.domains import get_domain_info print(get_domain_info('kieranwood.ca')) # {'creation_date': datetime.datetime(2018, 11, 6, 5, 9, 47), 'expiration_date': datetime.datetime(2020, 11, 6, 5, 9, 47), 'last_updated': datetime.datetime(2020, 1, 8, 8, 9, 44), 'name': 'kieranwood.ca', 'name_servers': {'kevin.ns.cloudflare.com', 'sharon.ns.cloudflare.com'}, 'registrant_cc': 'redacted for privacy', 'registrar': 'Go Daddy Domains Canada, Inc'} ``` """ logging.info(f"Entering get_domain_info(domain={domain})") _install_whois() # Verify/install whois # Validation ## Strip protocols if domain.startswith("https://"): logging.info(f"Stripping https:// protocol from {domain}") domain = domain.replace("https://", "") elif domain.startswith("http://"): logging.info(f"Stripping http:// protocol from {domain}") domain = domain.replace("http://", "") ## Raise error if subdomain logging.info(f"Confirming domain {domain} is not a subdomain") if len(domain.split(".")) > 2: # If domain is subdomain raise ValueError(f"Provided domain {domain} is likely a subdomain") ## Raise Error if invalid TLD try: logging.info(f"Querying {domain} with whois") domain_details = whois.query(domain) except Exception as e: if "Unknown TLD:" in str(e): raise ValueError(f"Domain {domain} is not a valid domain") # ## TODO: When new version of python-whois-extended releases uncomment below code # try: # if os.name == "nt" and os.path.exists(os.path.realpath(f"{os.getenv('USERPROFILE')}\\..\\..\\whois")): # INSTALL_FOLDER = os.path.realpath(f"{os.getenv('USERPROFILE')}\\..\\..\\whois\\whois.exe") # domain_details = whois.query(domain, executable=INSTALL_FOLDER) # else: # domain_details = whois.query(domain) # except Exception as e: # if "Unknown TLD:" in str(e): # raise ValueError(f"Domain {domain} is not a valid domain") # else: # raise e # Parse response if not domain_details: # If the domain is not registered logging.info(f"""Exiting get_domain_info() and returning {{'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': {domain}, 'name_servers': False, 'registrant_cc': False, 'registrar': False}}""") return {'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': domain, 'name_servers': False, 'registrant_cc': False, 'registrar': False} elif domain_details is None: # If the domain is not registered and query completely failed logging.info(f"""Exiting get_domain_info() and returning {{'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': {domain}, 'name_servers': False, 'registrant_cc': False, 'registrar': False}}""") return {'creation_date': False, 'expiration_date': False, 'last_updated': False, 'name': domain, 'name_servers': False, 'registrant_cc': False, 'registrar': False} else: # If there was domain info logging.info(f"Exiting get_domain_info() and returning {vars(domain_details)}") return vars(domain_details)