whois/parser.py
changeset 75 81859268375c
parent 67 25bab46f7281
parent 74 4f9e0d921642
child 83 a0a0ae15499b
child 87 0f12faf57d33
child 92 7d3efe9ad172
equal deleted inserted replaced
69:5e0cf54cf6c3 75:81859268375c
     3 # Copyright (c) 2008 Andrey Petrov
     3 # Copyright (c) 2008 Andrey Petrov
     4 #
     4 #
     5 # This module is part of pywhois and is released under
     5 # This module is part of pywhois and is released under
     6 # the MIT license: http://www.opensource.org/licenses/mit-license.php
     6 # the MIT license: http://www.opensource.org/licenses/mit-license.php
     7 
     7 
       
     8 from __future__ import absolute_import
       
     9 from __future__ import unicode_literals
       
    10 from __future__ import print_function
       
    11 from __future__ import division
       
    12 from future import standard_library
       
    13 standard_library.install_aliases()
       
    14 from builtins import *
       
    15 from builtins import str
       
    16 from past.builtins import basestring
     8 
    17 
     9 import json
    18 import json
    10 from datetime import datetime
    19 from datetime import datetime
    11 import re
    20 import re
    12 try:
    21 try:
    13     import dateutil.parser as dp
    22     import dateutil.parser as dp
    14     from time_zones import tz_data
    23     from .time_zones import tz_data
    15     DATEUTIL = True
    24     DATEUTIL = True
    16 except ImportError:
    25 except ImportError:
    17     DATEUTIL = False
    26     DATEUTIL = False
       
    27 
       
    28 EMAIL_REGEX = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"
    18 
    29 
    19 KNOWN_FORMATS = [
    30 KNOWN_FORMATS = [
    20     '%d-%b-%Y', 				# 02-jan-2000
    31     '%d-%b-%Y', 				# 02-jan-2000
    21     '%Y-%m-%d', 				# 2000-01-02
    32     '%Y-%m-%d', 				# 2000-01-02
    22     '%d.%m.%Y', 				# 2.1.2000
    33     '%d.%m.%Y', 				# 2.1.2000
    72     """Base class for parsing a Whois entries.
    83     """Base class for parsing a Whois entries.
    73     """
    84     """
    74     # regular expressions to extract domain data from whois profile
    85     # regular expressions to extract domain data from whois profile
    75     # child classes will override this
    86     # child classes will override this
    76     _regex = {
    87     _regex = {
    77         'domain_name':          'Domain Name:\s?(.+)',
    88         'domain_name':          'Domain Name: *(.+)',
    78         'registrar':            'Registrar:\s?(.+)',
    89         'registrar':            'Registrar: *(.+)',
    79         'whois_server':         'Whois Server:\s?(.+)',
    90         'whois_server':         'Whois Server: *(.+)',
    80         'referral_url':         'Referral URL:\s?(.+)',  # http url of whois_server
    91         'referral_url':         'Referral URL: *(.+)',  # http url of whois_server
    81         'updated_date':         'Updated Date:\s?(.+)',
    92         'updated_date':         'Updated Date: *(.+)',
    82         'creation_date':        'Creation Date:\s?(.+)',
    93         'creation_date':        'Creation Date: *(.+)',
    83         'expiration_date':      'Expir\w+ Date:\s?(.+)',
    94         'expiration_date':      'Expir\w+ Date: *(.+)',
    84         'name_servers':         'Name Server:\s?(.+)',  # list of name servers
    95         'name_servers':         'Name Server: *(.+)',  # list of name servers
    85         'status':               'Status:\s?(.+)',  # list of statuses
    96         'status':               'Status: *(.+)',  # list of statuses
    86         'emails':               '[\w.-]+@[\w.-]+\.[\w]{2,4}',  # list of email s
    97         'emails':               EMAIL_REGEX,  # list of email s
    87         'dnssec':               'dnssec:\s*([\S]+)',
    98         'dnssec':               'dnssec: *([\S]+)',
    88         'name':                 'Registrant Name:\s*(.+)',
    99         'name':                 'Registrant Name: *(.+)',
    89         'org':                  'Registrant\s*Organization:\s*(.+)',
   100         'org':                  'Registrant\s*Organization: *(.+)',
    90         'address':              'Registrant Street:\s*(.+)',
   101         'address':              'Registrant Street: *(.+)',
    91         'city':                 'Registrant City:\s*(.+)',
   102         'city':                 'Registrant City: *(.+)',
    92         'state':                'Registrant State/Province:\s*(.+)',
   103         'state':                'Registrant State/Province: *(.+)',
    93         'zipcode':              'Registrant Postal Code:\s*(.+)',
   104         'zipcode':              'Registrant Postal Code: *(.+)',
    94         'country':              'Registrant Country:\s*(.+)',
   105         'country':              'Registrant Country: *(.+)',
    95     }
   106     }
    96     dayfirst = False
   107     dayfirst = False
    97     yearfirst = False
   108     yearfirst = False
    98 
   109 
    99     def __init__(self, domain, text, regex=None):
   110     def __init__(self, domain, text, regex=None):
   108 
   119 
   109     def parse(self):
   120     def parse(self):
   110         """The first time an attribute is called it will be calculated here.
   121         """The first time an attribute is called it will be calculated here.
   111         The attribute is then set to be accessed directly by subsequent calls.
   122         The attribute is then set to be accessed directly by subsequent calls.
   112         """
   123         """
   113         for attr, regex in self._regex.items():
   124         for attr, regex in list(self._regex.items()):
   114             if regex:
   125             if regex:
   115                 values = []
   126                 values = []
   116                 for value in re.findall(regex, self.text, re.IGNORECASE):
   127                 for value in re.findall(regex, self.text, re.IGNORECASE):
   117                     value = value.strip()
   128                     value = value.strip()
   118                     if value and isinstance(value, basestring):
   129                     if value and isinstance(value, basestring):
   237 
   248 
   238 class WhoisOrg(WhoisEntry):
   249 class WhoisOrg(WhoisEntry):
   239     """Whois parser for .org domains
   250     """Whois parser for .org domains
   240     """
   251     """
   241     regex = {
   252     regex = {
   242         'domain_name':      'Domain Name:\s?(.+)',
   253         'domain_name':      'Domain Name: *(.+)',
   243         'registrar':        'Registrar:\s?(.+)',
   254         'registrar':        'Registrar: *(.+)',
   244         'whois_server':     'Whois Server:\s?(.+)', # empty usually
   255         'whois_server':     'Whois Server: *(.+)', # empty usually
   245         'referral_url':     'Referral URL:\s?(.+)', # http url of whois_server: empty usually
   256         'referral_url':     'Referral URL: *(.+)', # http url of whois_server: empty usually
   246         'updated_date':     'Updated Date:\s?(.+)',
   257         'updated_date':     'Updated Date: *(.+)',
   247         'creation_date':    'Creation Date:\s?(.+)',
   258         'creation_date':    'Creation Date: *(.+)',
   248         'expiration_date':  'Registry Expiry Date:\s?(.+)',
   259         'expiration_date':  'Registry Expiry Date: *(.+)',
   249         'name_servers':     'Name Server:\s?(.+)', # list of name servers
   260         'name_servers':     'Name Server: *(.+)', # list of name servers
   250         'status':           'Status:\s?(.+)', # list of statuses
   261         'status':           'Status: *(.+)', # list of statuses
   251         'emails':           '[\w.-]+@[\w.-]+\.[\w]{2,4}', # list of email addresses
   262         'emails':           EMAIL_REGEX, # list of email addresses
   252     }
   263     }
   253     
   264     
   254     def __init__(self, domain, text):
   265     def __init__(self, domain, text):
   255         if text.strip() == 'NOT FOUND':
   266         if text.strip() == 'NOT FOUND':
   256             raise PywhoisError(text)
   267             raise PywhoisError(text)
   260 
   271 
   261 class WhoisRu(WhoisEntry):
   272 class WhoisRu(WhoisEntry):
   262     """Whois parser for .ru domains
   273     """Whois parser for .ru domains
   263     """
   274     """
   264     regex = {
   275     regex = {
   265         'domain_name': 'domain:\s*(.+)',
   276         'domain_name': 'domain: *(.+)',
   266         'registrar': 'registrar:\s*(.+)',
   277         'registrar': 'registrar: *(.+)',
   267         'creation_date': 'created:\s*(.+)',
   278         'creation_date': 'created: *(.+)',
   268         'expiration_date': 'paid-till:\s*(.+)',
   279         'expiration_date': 'paid-till: *(.+)',
   269         'name_servers': 'nserver:\s*(.+)',  # list of name servers
   280         'name_servers': 'nserver: *(.+)',  # list of name servers
   270         'status': 'state:\s*(.+)',  # list of statuses
   281         'status': 'state: *(.+)',  # list of statuses
   271         'emails': '[\w.-]+@[\w.-]+\.[\w]{2,4}',  # list of email addresses
   282         'emails': EMAIL_REGEX,  # list of email addresses
   272         'org': 'org:\s*(.+)'
   283         'org': 'org: *(.+)'
   273     }
   284     }
   274 
   285 
   275     def __init__(self, domain, text):
   286     def __init__(self, domain, text):
   276         if text.strip() == 'No entries found':
   287         if text.strip() == 'No entries found':
   277             raise PywhoisError(text)
   288             raise PywhoisError(text)
   309 
   320 
   310 class WhoisName(WhoisEntry):
   321 class WhoisName(WhoisEntry):
   311     """Whois parser for .name domains
   322     """Whois parser for .name domains
   312     """
   323     """
   313     regex = {
   324     regex = {
   314         'domain_name_id':  'Domain Name ID:\s*(.+)',
   325         'domain_name_id':  'Domain Name ID: *(.+)',
   315         'domain_name':     'Domain Name:\s*(.+)',
   326         'domain_name':     'Domain Name: *(.+)',
   316         'registrar_id':    'Sponsoring Registrar ID:\s*(.+)',
   327         'registrar_id':    'Sponsoring Registrar ID: *(.+)',
   317         'registrar':       'Sponsoring Registrar:\s*(.+)',
   328         'registrar':       'Sponsoring Registrar: *(.+)',
   318         'registrant_id':   'Registrant ID:\s*(.+)',
   329         'registrant_id':   'Registrant ID: *(.+)',
   319         'admin_id':        'Admin ID:\s*(.+)',
   330         'admin_id':        'Admin ID: *(.+)',
   320         'technical_id':    'Tech ID:\s*(.+)',
   331         'technical_id':    'Tech ID: *(.+)',
   321         'billing_id':      'Billing ID:\s*(.+)',
   332         'billing_id':      'Billing ID: *(.+)',
   322         'creation_date':   'Created On:\s*(.+)',
   333         'creation_date':   'Created On: *(.+)',
   323         'expiration_date': 'Expires On:\s*(.+)',
   334         'expiration_date': 'Expires On: *(.+)',
   324         'updated_date':    'Updated On:\s*(.+)',
   335         'updated_date':    'Updated On: *(.+)',
   325         'name_server_ids': 'Name Server ID:\s*(.+)',  # list of name server ids
   336         'name_server_ids': 'Name Server ID: *(.+)',  # list of name server ids
   326         'name_servers':    'Name Server:\s*(.+)',  # list of name servers
   337         'name_servers':    'Name Server: *(.+)',  # list of name servers
   327         'status':          'Domain Status:\s*(.+)',  # list of statuses
   338         'status':          'Domain Status: *(.+)',  # list of statuses
   328     }
   339     }
   329 
   340 
   330     def __init__(self, domain, text):
   341     def __init__(self, domain, text):
   331         if 'No match for ' in text:
   342         if 'No match for ' in text:
   332             raise PywhoisError(text)
   343             raise PywhoisError(text)
   336 
   347 
   337 class WhoisUs(WhoisEntry):
   348 class WhoisUs(WhoisEntry):
   338     """Whois parser for .us domains
   349     """Whois parser for .us domains
   339     """
   350     """
   340     regex = {
   351     regex = {
   341         'domain_name':                    'Domain Name:\s*(.+)',
   352         'domain_name':                    'Domain Name: *(.+)',
   342         'domain__id':                     'Domain ID:\s*(.+)',
   353         'domain__id':                     'Domain ID: *(.+)',
   343         'registrar':                      'Sponsoring Registrar:\s*(.+)',
   354         'registrar':                      'Sponsoring Registrar: *(.+)',
   344         'registrar_id':                   'Sponsoring Registrar IANA ID:\s*(.+)',
   355         'registrar_id':                   'Sponsoring Registrar IANA ID: *(.+)',
   345         'registrar_url':                  'Registrar URL \(registration services\):\s*(.+)',
   356         'registrar_url':                  'Registrar URL \(registration services\): *(.+)',
   346         'status':                         'Domain Status:\s*(.+)',  # list of statuses
   357         'status':                         'Domain Status: *(.+)',  # list of statuses
   347         'registrant_id':                  'Registrant ID:\s*(.+)',
   358         'registrant_id':                  'Registrant ID: *(.+)',
   348         'registrant_name':                'Registrant Name:\s*(.+)',
   359         'registrant_name':                'Registrant Name: *(.+)',
   349         'registrant_address1':            'Registrant Address1:\s*(.+)',
   360         'registrant_address1':            'Registrant Address1: *(.+)',
   350         'registrant_address2':            'Registrant Address2:\s*(.+)',
   361         'registrant_address2':            'Registrant Address2: *(.+)',
   351         'registrant_city':                'Registrant City:\s*(.+)',
   362         'registrant_city':                'Registrant City: *(.+)',
   352         'registrant_state_province':      'Registrant State/Province:\s*(.+)',
   363         'registrant_state_province':      'Registrant State/Province: *(.+)',
   353         'registrant_postal_code':         'Registrant Postal Code:\s*(.+)',
   364         'registrant_postal_code':         'Registrant Postal Code: *(.+)',
   354         'registrant_country':             'Registrant Country:\s*(.+)',
   365         'registrant_country':             'Registrant Country: *(.+)',
   355         'registrant_country_code':        'Registrant Country Code:\s*(.+)',
   366         'registrant_country_code':        'Registrant Country Code: *(.+)',
   356         'registrant_phone_number':        'Registrant Phone Number:\s*(.+)',
   367         'registrant_phone_number':        'Registrant Phone Number: *(.+)',
   357         'registrant_email':               'Registrant Email:\s*(.+)',
   368         'registrant_email':               'Registrant Email: *(.+)',
   358         'registrant_application_purpose': 'Registrant Application Purpose:\s*(.+)',
   369         'registrant_application_purpose': 'Registrant Application Purpose: *(.+)',
   359         'registrant_nexus_category':      'Registrant Nexus Category:\s*(.+)',
   370         'registrant_nexus_category':      'Registrant Nexus Category: *(.+)',
   360         'admin_id':                       'Administrative Contact ID:\s*(.+)',
   371         'admin_id':                       'Administrative Contact ID: *(.+)',
   361         'admin_name':                     'Administrative Contact Name:\s*(.+)',
   372         'admin_name':                     'Administrative Contact Name: *(.+)',
   362         'admin_address1':                 'Administrative Contact Address1:\s*(.+)',
   373         'admin_address1':                 'Administrative Contact Address1: *(.+)',
   363         'admin_address2':                 'Administrative Contact Address2:\s*(.+)',
   374         'admin_address2':                 'Administrative Contact Address2: *(.+)',
   364         'admin_city':                     'Administrative Contact City:\s*(.+)',
   375         'admin_city':                     'Administrative Contact City: *(.+)',
   365         'admin_state_province':           'Administrative Contact State/Province:\s*(.+)',
   376         'admin_state_province':           'Administrative Contact State/Province: *(.+)',
   366         'admin_postal_code':              'Administrative Contact Postal Code:\s*(.+)',
   377         'admin_postal_code':              'Administrative Contact Postal Code: *(.+)',
   367         'admin_country':                  'Administrative Contact Country:\s*(.+)',
   378         'admin_country':                  'Administrative Contact Country: *(.+)',
   368         'admin_country_code':             'Administrative Contact Country Code:\s*(.+)',
   379         'admin_country_code':             'Administrative Contact Country Code: *(.+)',
   369         'admin_phone_number':             'Administrative Contact Phone Number:\s*(.+)',
   380         'admin_phone_number':             'Administrative Contact Phone Number: *(.+)',
   370         'admin_email':                    'Administrative Contact Email:\s*(.+)',
   381         'admin_email':                    'Administrative Contact Email: *(.+)',
   371         'admin_application_purpose':      'Administrative Application Purpose:\s*(.+)',
   382         'admin_application_purpose':      'Administrative Application Purpose: *(.+)',
   372         'admin_nexus_category':           'Administrative Nexus Category:\s*(.+)',
   383         'admin_nexus_category':           'Administrative Nexus Category: *(.+)',
   373         'billing_id':                     'Billing Contact ID:\s*(.+)',
   384         'billing_id':                     'Billing Contact ID: *(.+)',
   374         'billing_name':                   'Billing Contact Name:\s*(.+)',
   385         'billing_name':                   'Billing Contact Name: *(.+)',
   375         'billing_address1':               'Billing Contact Address1:\s*(.+)',
   386         'billing_address1':               'Billing Contact Address1: *(.+)',
   376         'billing_address2':               'Billing Contact Address2:\s*(.+)',
   387         'billing_address2':               'Billing Contact Address2: *(.+)',
   377         'billing_city':                   'Billing Contact City:\s*(.+)',
   388         'billing_city':                   'Billing Contact City: *(.+)',
   378         'billing_state_province':         'Billing Contact State/Province:\s*(.+)',
   389         'billing_state_province':         'Billing Contact State/Province: *(.+)',
   379         'billing_postal_code':            'Billing Contact Postal Code:\s*(.+)',
   390         'billing_postal_code':            'Billing Contact Postal Code: *(.+)',
   380         'billing_country':                'Billing Contact Country:\s*(.+)',
   391         'billing_country':                'Billing Contact Country: *(.+)',
   381         'billing_country_code':           'Billing Contact Country Code:\s*(.+)',
   392         'billing_country_code':           'Billing Contact Country Code: *(.+)',
   382         'billing_phone_number':           'Billing Contact Phone Number:\s*(.+)',
   393         'billing_phone_number':           'Billing Contact Phone Number: *(.+)',
   383         'billing_email':                  'Billing Contact Email:\s*(.+)',
   394         'billing_email':                  'Billing Contact Email: *(.+)',
   384         'billing_application_purpose':    'Billing Application Purpose:\s*(.+)',
   395         'billing_application_purpose':    'Billing Application Purpose: *(.+)',
   385         'billing_nexus_category':         'Billing Nexus Category:\s*(.+)',
   396         'billing_nexus_category':         'Billing Nexus Category: *(.+)',
   386         'tech_id':                        'Technical Contact ID:\s*(.+)',
   397         'tech_id':                        'Technical Contact ID: *(.+)',
   387         'tech_name':                      'Technical Contact Name:\s*(.+)',
   398         'tech_name':                      'Technical Contact Name: *(.+)',
   388         'tech_address1':                  'Technical Contact Address1:\s*(.+)',
   399         'tech_address1':                  'Technical Contact Address1: *(.+)',
   389         'tech_address2':                  'Technical Contact Address2:\s*(.+)',
   400         'tech_address2':                  'Technical Contact Address2: *(.+)',
   390         'tech_city':                      'Technical Contact City:\s*(.+)',
   401         'tech_city':                      'Technical Contact City: *(.+)',
   391         'tech_state_province':            'Technical Contact State/Province:\s*(.+)',
   402         'tech_state_province':            'Technical Contact State/Province: *(.+)',
   392         'tech_postal_code':               'Technical Contact Postal Code:\s*(.+)',
   403         'tech_postal_code':               'Technical Contact Postal Code: *(.+)',
   393         'tech_country':                   'Technical Contact Country:\s*(.+)',
   404         'tech_country':                   'Technical Contact Country: *(.+)',
   394         'tech_country_code':              'Technical Contact Country Code:\s*(.+)',
   405         'tech_country_code':              'Technical Contact Country Code: *(.+)',
   395         'tech_phone_number':              'Technical Contact Phone Number:\s*(.+)',
   406         'tech_phone_number':              'Technical Contact Phone Number: *(.+)',
   396         'tech_email':                     'Technical Contact Email:\s*(.+)',
   407         'tech_email':                     'Technical Contact Email: *(.+)',
   397         'tech_application_purpose':       'Technical Application Purpose:\s*(.+)',
   408         'tech_application_purpose':       'Technical Application Purpose: *(.+)',
   398         'tech_nexus_category':            'Technical Nexus Category:\s*(.+)',
   409         'tech_nexus_category':            'Technical Nexus Category: *(.+)',
   399         'name_servers':                   'Name Server:\s*(.+)',  # list of name servers
   410         'name_servers':                   'Name Server: *(.+)',  # list of name servers
   400         'created_by_registrar':           'Created by Registrar:\s*(.+)',
   411         'created_by_registrar':           'Created by Registrar: *(.+)',
   401         'last_updated_by_registrar':      'Last Updated by Registrar:\s*(.+)',
   412         'last_updated_by_registrar':      'Last Updated by Registrar: *(.+)',
   402         'creation_date':                  'Domain Registration Date:\s*(.+)',
   413         'creation_date':                  'Domain Registration Date: *(.+)',
   403         'expiration_date':                'Domain Expiration Date:\s*(.+)',
   414         'expiration_date':                'Domain Expiration Date: *(.+)',
   404         'updated_date':                   'Domain Last Updated Date:\s*(.+)',
   415         'updated_date':                   'Domain Last Updated Date: *(.+)',
   405     }
   416     }
   406 
   417 
   407     def __init__(self, domain, text):
   418     def __init__(self, domain, text):
   408         if 'Not found:' in text:
   419         if 'Not found:' in text:
   409             raise PywhoisError(text)
   420             raise PywhoisError(text)
   413 
   424 
   414 class WhoisPl(WhoisEntry):
   425 class WhoisPl(WhoisEntry):
   415     """Whois parser for .pl domains
   426     """Whois parser for .pl domains
   416     """
   427     """
   417     regex = {
   428     regex = {
   418         'domain_name':                    'DOMAIN NAME:\s*(.+)\n',
   429         'domain_name':                    'DOMAIN NAME: *(.+)\n',
   419         'registrar':                      'REGISTRAR:\n\s*(.+)',
   430         'registrar':                      'REGISTRAR:\n\s*(.+)',
   420         'registrar_url':                  'URL:\s*(.+)',        # not available
   431         'registrar_url':                  'URL: *(.+)',        # not available
   421         'status':                         'Registration status:\n\s*(.+)',  # not available
   432         'status':                         'Registration status:\n\s*(.+)',  # not available
   422         'registrant_name':                'Registrant:\n\s*(.+)',   # not available
   433         'registrant_name':                'Registrant:\n\s*(.+)',   # not available
   423         'creation_date':                  'created:\s*(.+)\n',
   434         'creation_date':                  'created: *(.+)\n',
   424         'expiration_date':                'renewal date:\s*(.+)',
   435         'expiration_date':                'renewal date: *(.+)',
   425         'updated_date':                   'last modified:\s*(.+)\n',
   436         'updated_date':                   'last modified: *(.+)\n',
   426     }
   437     }
   427 
   438 
   428     def __init__(self, domain, text):
   439     def __init__(self, domain, text):
   429         if 'No information available about domain name' in text:
   440         if 'No information available about domain name' in text:
   430             raise PywhoisError(text)
   441             raise PywhoisError(text)
   434 
   445 
   435 class WhoisCa(WhoisEntry):
   446 class WhoisCa(WhoisEntry):
   436     """Whois parser for .ca domains
   447     """Whois parser for .ca domains
   437     """
   448     """
   438     regex = {
   449     regex = {
   439         'registrant_name':                'Name:\s*(.+)',
   450         'registrant_name':                'Name: *(.+)',
   440         'registrant_number':              'Number:\s*(.+)\n',
   451         'registrant_number':              'Number: *(.+)\n',
   441     }
   452     }
   442 
   453 
   443     def __init__(self, domain, text):
   454     def __init__(self, domain, text):
   444         if 'Domain status:         available' in text:
   455         if 'Domain status:         available' in text:
   445             raise PywhoisError(text)
   456             raise PywhoisError(text)
   525     """Whois parser for .uk domains
   536     """Whois parser for .uk domains
   526     """
   537     """
   527     regex = {
   538     regex = {
   528         'domain_name':                    'Domain name:\n\s*(.+)',
   539         'domain_name':                    'Domain name:\n\s*(.+)',
   529         'registrar':                      'Registrar:\n\s*(.+)',
   540         'registrar':                      'Registrar:\n\s*(.+)',
   530         'registrar_url':                  'URL:\s*(.+)',
   541         'registrar_url':                  'URL: *(.+)',
   531         'status':                         'Registration status:\n\s*(.+)',  # list of statuses
   542         'status':                         'Registration status:\n\s*(.+)',  # list of statuses
   532         'registrant_name':                'Registrant:\n\s*(.+)',
   543         'registrant_name':                'Registrant:\n\s*(.+)',
   533         'creation_date':                  'Registered on:\s*(.+)',
   544         'creation_date':                  'Registered on: *(.+)',
   534         'expiration_date':                'Expiry date:\s*(.+)',
   545         'expiration_date':                'Expiry date: *(.+)',
   535         'updated_date':                   'Last updated:\s*(.+)',
   546         'updated_date':                   'Last updated: *(.+)',
   536         'name_servers':                   'Name servers:\s*(.+)',
   547         'name_servers':                   'Name servers: *(.+)',
   537     }
   548     }
   538 
   549 
   539     def __init__(self, domain, text):
   550     def __init__(self, domain, text):
   540         if 'No match for ' in text:
   551         if 'No match for ' in text:
   541             raise PywhoisError(text)
   552             raise PywhoisError(text)
   545 
   556 
   546 class WhoisFr(WhoisEntry):
   557 class WhoisFr(WhoisEntry):
   547     """Whois parser for .fr domains
   558     """Whois parser for .fr domains
   548     """
   559     """
   549     regex = {
   560     regex = {
   550         'domain_name': 'domain:\s*(.+)',
   561         'domain_name': 'domain: *(.+)',
   551         'registrar': 'registrar:\s*(.+)',
   562         'registrar': 'registrar: *(.+)',
   552         'creation_date': 'created:\s*(.+)',
   563         'creation_date': 'created: *(.+)',
   553         'expiration_date': 'Expir\w+ Date:\s?(.+)',
   564         'expiration_date': 'anniversary: *(.+)',
   554         'name_servers': 'nserver:\s*(.+)',  # list of name servers
   565         'name_servers': 'nserver: *(.+)',  # list of name servers
   555         'status': 'status:\s*(.+)',  # list of statuses
   566         'status': 'status: *(.+)',  # list of statuses
   556         'emails': '[\w.-]+@[\w.-]+\.[\w]{2,4}',  # list of email addresses
   567         'emails': EMAIL_REGEX,  # list of email addresses
   557         'updated_date': 'last-update:\s*(.+)',
   568         'updated_date': 'last-update: *(.+)',
   558     }
   569     }
   559 
   570 
   560     def __init__(self, domain, text):
   571     def __init__(self, domain, text):
   561         if 'No entries found' in text:
   572         if 'No entries found' in text:
   562             raise PywhoisError(text)
   573             raise PywhoisError(text)
   566 
   577 
   567 class WhoisFi(WhoisEntry):
   578 class WhoisFi(WhoisEntry):
   568     """Whois parser for .fi domains
   579     """Whois parser for .fi domains
   569     """
   580     """
   570     regex = {
   581     regex = {
   571         'domain_name':                    'domain:\s*([\S]+)',
   582         'domain_name':                    'domain: *([\S]+)',
   572         'name':                           'descr:\s*([\S\ ]+)',
   583         'name':                           'descr: *([\S\ ]+)',
   573         'address':                        'address:\s*([\S\ ]+)',
   584         'address':                        'address: *([\S\ ]+)',
   574         'phone':                          'phone:\s*([\S\ ]+)',
   585         'phone':                          'phone: *([\S\ ]+)',
   575         'status':                         'status:\s*([\S]+)',  # list of statuses
   586         'status':                         'status: *([\S]+)',  # list of statuses
   576         'creation_date':                  'created:\s*([\S]+)',
   587         'creation_date':                  'created: *([\S]+)',
   577         'updated_date':                   'modified:\s*([\S]+)',
   588         'updated_date':                   'modified: *([\S]+)',
   578         'expiration_date':                'expires:\s*([\S]+)',
   589         'expiration_date':                'expires: *([\S]+)',
   579         'name_servers':                   'nserver:\s*([\S]+) \[\S+\]',  # list of name servers
   590         'name_servers':                   'nserver: *([\S]+) \[\S+\]',  # list of name servers
   580         'name_server_statuses':           'nserver:\s*([\S]+) \[(\S+)\]',  # list of name servers and statuses
   591         'name_server_statuses':           'nserver: *([\S]+) \[(\S+)\]',  # list of name servers and statuses
   581         'dnssec':                         'dnssec:\s*([\S]+)',
   592         'dnssec':                         'dnssec: *([\S]+)',
   582     }
   593     }
   583 
   594 
   584     def __init__(self, domain, text):
   595     def __init__(self, domain, text):
   585         if 'Domain not ' in text:
   596         if 'Domain not ' in text:
   586             raise PywhoisError(text)
   597             raise PywhoisError(text)
   609 
   620 
   610 class WhoisAU(WhoisEntry):
   621 class WhoisAU(WhoisEntry):
   611     """Whois parser for .au domains
   622     """Whois parser for .au domains
   612     """
   623     """
   613     regex = {
   624     regex = {
   614         'domain_name':                    'Domain Name:\s*(.+)\n',
   625         'domain_name':                    'Domain Name: *(.+)\n',
   615         'last_modified':			      'Last Modified:\s*(.+)\n',
   626         'last_modified':			      'Last Modified: *(.+)\n',
   616         'registrar':                      'Registrar Name:\s*(.+)\n',
   627         'registrar':                      'Registrar Name: *(.+)\n',
   617         'status':                         'Status:\s*(.+)',
   628         'status':                         'Status: *(.+)',
   618         'registrant_name':                'Registrant:\s*(.+)',
   629         'registrant_name':                'Registrant: *(.+)',
   619         'name_servers':                   'Name Server:\s*(.+)',
   630         'name_servers':                   'Name Server: *(.+)',
   620     }
   631     }
   621 
   632 
   622     def __init__(self, domain, text):
   633     def __init__(self, domain, text):
   623         if text.strip() == 'No Data Found':
   634         if text.strip() == 'No Data Found':
   624             raise PywhoisError(text)
   635             raise PywhoisError(text)
   628 
   639 
   629 class WhoisEu(WhoisEntry):
   640 class WhoisEu(WhoisEntry):
   630     """Whois parser for .eu domains
   641     """Whois parser for .eu domains
   631     """
   642     """
   632     regex = {
   643     regex = {
   633         'domain_name': r'Domain:\s*([^\n\r]+)',
   644         'domain_name': r'Domain: *([^\n\r]+)',
   634         'tech_name': r'Technical:\s*Name:\s*([^\n\r]+)',
   645         'tech_name': r'Technical: *Name: *([^\n\r]+)',
   635         'tech_org': r'Technical:\s*Name:\s*[^\n\r]+\s*Organisation:\s*([^\n\r]+)',
   646         'tech_org': r'Technical: *Name: *[^\n\r]+\s*Organisation: *([^\n\r]+)',
   636         'tech_phone': r'Technical:\s*Name:\s*[^\n\r]+\s*Organisation:\s*[^\n\r]+\s*Language:\s*[^\n\r]+\s*Phone:\s*([^\n\r]+)',
   647         'tech_phone': r'Technical: *Name: *[^\n\r]+\s*Organisation: *[^\n\r]+\s*Language: *[^\n\r]+\s*Phone: *([^\n\r]+)',
   637         'tech_fax': r'Technical:\s*Name:\s*[^\n\r]+\s*Organisation:\s*[^\n\r]+\s*Language:\s*[^\n\r]+\s*Phone:\s*[^\n\r]+\s*Fax:\s*([^\n\r]+)',
   648         'tech_fax': r'Technical: *Name: *[^\n\r]+\s*Organisation: *[^\n\r]+\s*Language: *[^\n\r]+\s*Phone: *[^\n\r]+\s*Fax: *([^\n\r]+)',
   638         'tech_email': r'Technical:\s*Name:\s*[^\n\r]+\s*Organisation:\s*[^\n\r]+\s*Language:\s*[^\n\r]+\s*Phone:\s*[^\n\r]+\s*Fax:\s*[^\n\r]+\s*Email:\s*([^\n\r]+)',
   649         'tech_email': r'Technical: *Name: *[^\n\r]+\s*Organisation: *[^\n\r]+\s*Language: *[^\n\r]+\s*Phone: *[^\n\r]+\s*Fax: *[^\n\r]+\s*Email: *([^\n\r]+)',
   639         'registrar': r'Registrar:\s*Name:\s*([^\n\r]+)',
   650         'registrar': r'Registrar: *Name: *([^\n\r]+)',
   640         'name_servers': r'Name servers:\s*([^\n\r]+)\s*([^\n\r]*)',  # list of name servers
   651         'name_servers': r'Name servers: *([^\n\r]+)\s*([^\n\r]*)',  # list of name servers
   641     }
   652     }
   642 
   653 
   643     def __init__(self, domain, text):
   654     def __init__(self, domain, text):
   644         if text.strip() == 'Status: AVAILABLE':
   655         if text.strip() == 'Status: AVAILABLE':
   645             raise PywhoisError(text)
   656             raise PywhoisError(text)
   649 
   660 
   650 class WhoisBr(WhoisEntry):
   661 class WhoisBr(WhoisEntry):
   651     """Whois parser for .br domains
   662     """Whois parser for .br domains
   652     """
   663     """
   653     regex = {
   664     regex = {
   654         'domain':                        'domain:\s*(.+)\n',
   665         'domain':                        'domain: *(.+)\n',
   655         'owner':                         'owner:\s*([\S ]+)',
   666         'owner':                         'owner: *([\S ]+)',
   656         'ownerid':                       'ownerid:\s*(.+)',
   667         'ownerid':                       'ownerid: *(.+)',
   657         'country':                       'country:\s*(.+)',
   668         'country':                       'country: *(.+)',
   658         'owner_c':                       'owner-c:\s*(.+)',
   669         'owner_c':                       'owner-c: *(.+)',
   659         'admin_c':                       'admin-c:\s*(.+)',
   670         'admin_c':                       'admin-c: *(.+)',
   660         'tech_c':                        'tech-c:\s*(.+)',
   671         'tech_c':                        'tech-c: *(.+)',
   661         'billing_c':                     'billing-c:\s*(.+)',
   672         'billing_c':                     'billing-c: *(.+)',
   662         'nserver':                       'nserver:\s*(.+)',
   673         'nserver':                       'nserver: *(.+)',
   663         'nsstat':                        'nsstat:\s*(.+)',
   674         'nsstat':                        'nsstat: *(.+)',
   664         'nslastaa':                      'nslastaa:\s*(.+)',
   675         'nslastaa':                      'nslastaa: *(.+)',
   665         'saci':                          'saci:\s*(.+)',
   676         'saci':                          'saci: *(.+)',
   666         'created':                       'created:\s*(.+)',
   677         'created':                       'created: *(.+)',
   667         'expires':                       'expires:\s*(.+)',
   678         'expires':                       'expires: *(.+)',
   668         'changed':                       'changed:\s*(.+)',
   679         'changed':                       'changed: *(.+)',
   669         'status':                        'status:\s*(.+)',
   680         'status':                        'status: *(.+)',
   670         'nic_hdl_br':                    'nic-hdl-br:\s*(.+)',
   681         'nic_hdl_br':                    'nic-hdl-br: *(.+)',
   671         'person':                        'person:\s*([\S ]+)',
   682         'person':                        'person: *([\S ]+)',
   672         'email':                         'e-mail:\s*(.+)',
   683         'email':                         'e-mail: *(.+)',
   673     }
   684     }
   674 
   685 
   675     def __init__(self, domain, text):
   686     def __init__(self, domain, text):
   676 
   687 
   677         if 'Not found:' in text:
   688         if 'Not found:' in text:
   682 
   693 
   683 class WhoisKr(WhoisEntry):
   694 class WhoisKr(WhoisEntry):
   684     """Whois parser for .kr domains
   695     """Whois parser for .kr domains
   685     """
   696     """
   686     regex = {
   697     regex = {
   687         'domain_name': 'Domain Name\s*:\s*(.+)',
   698         'domain_name': 'Domain Name\s*: *(.+)',
   688         'registrant_org': 'Registrant\s*:\s*(.+)',
   699         'registrant_org': 'Registrant\s*: *(.+)',
   689         'registrant_address': 'Registrant Address\s*:\s*(.+)',
   700         'registrant_address': 'Registrant Address\s*: *(.+)',
   690         'registrant_zip': 'Registrant Zip Code\s*:\s*(.+)',
   701         'registrant_zip': 'Registrant Zip Code\s*: *(.+)',
   691         'admin_name': 'Administrative Contact\(AC\)\s*:\s*(.+)',
   702         'admin_name': 'Administrative Contact\(AC\)\s*: *(.+)',
   692         'admin_email': 'AC E-Mail\s*:\s*(.+)',
   703         'admin_email': 'AC E-Mail\s*: *(.+)',
   693         'admin_phone': 'AC Phone Number\s*:\s*(.+)',
   704         'admin_phone': 'AC Phone Number\s*: *(.+)',
   694         'creation_date': 'Registered Date\s*:\s*(.+)',
   705         'creation_date': 'Registered Date\s*: *(.+)',
   695         'updated_date':  'Last updated Date\s*:\s*(.+)',
   706         'updated_date':  'Last updated Date\s*: *(.+)',
   696         'expiration_date':  'Expiration Date\s*:\s*(.+)',
   707         'expiration_date':  'Expiration Date\s*: *(.+)',
   697         'registrar':  'Authorized Agency\s*:\s*(.+)',
   708         'registrar':  'Authorized Agency\s*: *(.+)',
   698         'name_servers': 'Host Name\s*:\s*(.+)',  # list of name servers
   709         'name_servers': 'Host Name\s*: *(.+)',  # list of name servers
   699     }
   710     }
   700 
   711 
   701     def __init__(self, domain, text):
   712     def __init__(self, domain, text):
   702         if text.endswith(' no match'):
   713         if text.endswith(' no match'):
   703             raise PywhoisError(text)
   714             raise PywhoisError(text)
   707 
   718 
   708 class WhoisPt(WhoisEntry):
   719 class WhoisPt(WhoisEntry):
   709     """Whois parser for .pt domains
   720     """Whois parser for .pt domains
   710     """
   721     """
   711     regex = {
   722     regex = {
   712         'domain_name': 'domain name:\s*(.+)',
   723         'domain_name': 'domain name: *(.+)',
   713         'creation_date': 'creation date \(dd\/mm\/yyyy\):\s*(.+)',
   724         'creation_date': 'creation date \(dd\/mm\/yyyy\): *(.+)',
   714         'expiration_date': 'expiration date \(dd\/mm\/yyyy\):\s*(.+)',
   725         'expiration_date': 'expiration date \(dd\/mm\/yyyy\): *(.+)',
   715         'name_servers': '\tNS\t(.+).',  # list of name servers
   726         'name_servers': '\tNS\t(.+).',  # list of name servers
   716         'status': 'status:\s*(.+)',  # list of statuses
   727         'status': 'status: *(.+)',  # list of statuses
   717         'emails': '[\w.-]+@[\w.-]+\.[\w]{2,4}',  # list of email addresses
   728         'emails': EMAIL_REGEX,  # list of email addresses
   718     }
   729     }
   719 
   730 
   720     def __init__(self, domain, text):
   731     def __init__(self, domain, text):
   721         if text.strip() == 'No entries found':
   732         if text.strip() == 'No entries found':
   722             raise PywhoisError(text)
   733             raise PywhoisError(text)
   726 
   737 
   727 class WhoisBg(WhoisEntry):
   738 class WhoisBg(WhoisEntry):
   728     """Whois parser for .bg domains
   739     """Whois parser for .bg domains
   729     """
   740     """
   730     regex = {
   741     regex = {
   731         'expiration_date': 'expires at:\s*(.+)',
   742         'expiration_date': 'expires at: *(.+)',
   732     }
   743     }
   733 
   744 
   734     dayfirst = True
   745     dayfirst = True
   735 
   746 
   736     def __init__(self, domain, text):
   747     def __init__(self, domain, text):
   742 
   753 
   743 class WhoisDe(WhoisEntry):
   754 class WhoisDe(WhoisEntry):
   744     """Whois parser for .de domains
   755     """Whois parser for .de domains
   745     """
   756     """
   746     regex = {
   757     regex = {
   747         'name': 'name:\s*(.+)',
   758         'name': 'name: *(.+)',
   748         'org': 'Organisation:\s*(.+)',
   759         'org': 'Organisation: *(.+)',
   749         'address': 'Address:\s*(.+)',
   760         'address': 'Address: *(.+)',
   750         'zipcode': 'PostalCode:\s*(.+)',
   761         'zipcode': 'PostalCode: *(.+)',
   751         'city': 'City:\s*(.+)',
   762         'city': 'City: *(.+)',
   752         'country_code': 'CountryCode:\s*(.+)',
   763         'country_code': 'CountryCode: *(.+)',
   753         'phone': 'Phone:\s*(.+)',
   764         'phone': 'Phone: *(.+)',
   754         'fax': 'Fax:\s*(.+)'
   765         'fax': 'Fax: *(.+)'
   755     }
   766     }
   756 
   767 
   757     def __init__(self, domain, text):
   768     def __init__(self, domain, text):
   758         if 'Status: free' in text:
   769         if 'Status: free' in text:
   759             raise PywhoisError(text)
   770             raise PywhoisError(text)
   763 
   774 
   764 class WhoisBe(WhoisEntry):
   775 class WhoisBe(WhoisEntry):
   765     """Whois parser for .be domains
   776     """Whois parser for .be domains
   766     """
   777     """
   767     regex = {
   778     regex = {
   768         'name': 'Name:\s*(.+)',
   779         'name': 'Name: *(.+)',
   769         'org': 'Organisation:\s*(.+)',
   780         'org': 'Organisation: *(.+)',
   770         'phone': 'Phone:\s*(.+)',
   781         'phone': 'Phone: *(.+)',
   771         'fax': 'Fax:\s*(.+)',
   782         'fax': 'Fax: *(.+)',
   772         'email': 'Email:\s*(.+)',
   783         'email': 'Email: *(.+)',
   773     }
   784     }
   774 
   785 
   775     def __init__(self, domain, text):
   786     def __init__(self, domain, text):
   776         if 'Status: AVAILABLE' in text:
   787         if 'Status: AVAILABLE' in text:
   777             raise PywhoisError(text)
   788             raise PywhoisError(text)
   782 
   793 
   783 class WhoisInfo(WhoisEntry):
   794 class WhoisInfo(WhoisEntry):
   784     """Whois parser for .info domains
   795     """Whois parser for .info domains
   785     """
   796     """
   786     regex = {
   797     regex = {
   787         'domain_name':      'Domain Name:\s?(.+)',
   798         'domain_name':      'Domain Name: *(.+)',
   788         'registrar':        'Registrar:\s?(.+)',
   799         'registrar':        'Registrar: *(.+)',
   789         'whois_server':     'Whois Server:\s?(.+)', # empty usually
   800         'whois_server':     'Whois Server: *(.+)', # empty usually
   790         'referral_url':     'Referral URL:\s?(.+)', # http url of whois_server: empty usually
   801         'referral_url':     'Referral URL: *(.+)', # http url of whois_server: empty usually
   791         'updated_date':     'Updated Date:\s?(.+)',
   802         'updated_date':     'Updated Date: *(.+)',
   792         'creation_date':    'Creation Date:\s?(.+)',
   803         'creation_date':    'Creation Date: *(.+)',
   793         'expiration_date':  'Registry Expiry Date:\s?(.+)',
   804         'expiration_date':  'Registry Expiry Date: *(.+)',
   794         'name_servers':     'Name Server:\s?(.+)', # list of name servers
   805         'name_servers':     'Name Server: *(.+)', # list of name servers
   795         'status':           'Status:\s?(.+)', # list of statuses
   806         'status':           'Status: *(.+)', # list of statuses
   796         'emails':           '[\w.-]+@[\w.-]+\.[\w]{2,4}', # list of email addresses
   807         'emails':           EMAIL_REGEX, # list of email addresses
   797         'name':             'Registrant Name:\s*(.+)',
   808         'name':             'Registrant Name: *(.+)',
   798         'org':              'Registrant Organization:\s*(.+)',
   809         'org':              'Registrant Organization: *(.+)',
   799         'address':          'Registrant Street:\s*(.+)',
   810         'address':          'Registrant Street: *(.+)',
   800         'city':             'Registrant City:\s*(.+)',
   811         'city':             'Registrant City: *(.+)',
   801         'state':            'Registrant State/Province:\s*(.+)',
   812         'state':            'Registrant State/Province: *(.+)',
   802         'zipcode':          'Registrant Postal Code:\s*(.+)',
   813         'zipcode':          'Registrant Postal Code: *(.+)',
   803         'country':          'Registrant Country:\s*(.+)',
   814         'country':          'Registrant Country: *(.+)',
   804     }
   815     }
   805 
   816 
   806     def __init__(self, domain, text):
   817     def __init__(self, domain, text):
   807         if text.strip() == 'NOT FOUND':
   818         if text.strip() == 'NOT FOUND':
   808             raise PywhoisError(text)
   819             raise PywhoisError(text)
   826 
   837 
   827 class WhoisClub(WhoisEntry):
   838 class WhoisClub(WhoisEntry):
   828     """Whois parser for .us domains
   839     """Whois parser for .us domains
   829     """
   840     """
   830     regex = {
   841     regex = {
   831         'domain_name':                    'Domain Name:\s*(.+)',
   842         'domain_name':                    'Domain Name: *(.+)',
   832         'domain__id':                     'Domain ID:\s*(.+)',
   843         'domain__id':                     'Domain ID: *(.+)',
   833         'registrar':                      'Sponsoring Registrar:\s*(.+)',
   844         'registrar':                      'Sponsoring Registrar: *(.+)',
   834         'registrar_id':                   'Sponsoring Registrar IANA ID:\s*(.+)',
   845         'registrar_id':                   'Sponsoring Registrar IANA ID: *(.+)',
   835         'registrar_url':                  'Registrar URL \(registration services\):\s*(.+)',
   846         'registrar_url':                  'Registrar URL \(registration services\): *(.+)',
   836         # list of statuses
   847         # list of statuses
   837         'status':                         'Domain Status:\s*(.+)',
   848         'status':                         'Domain Status: *(.+)',
   838         'registrant_id':                  'Registrant ID:\s*(.+)',
   849         'registrant_id':                  'Registrant ID: *(.+)',
   839         'registrant_name':                'Registrant Name:\s*(.+)',
   850         'registrant_name':                'Registrant Name: *(.+)',
   840         'registrant_address1':            'Registrant Address1:\s*(.+)',
   851         'registrant_address1':            'Registrant Address1: *(.+)',
   841         'registrant_address2':            'Registrant Address2:\s*(.+)',
   852         'registrant_address2':            'Registrant Address2: *(.+)',
   842         'registrant_city':                'Registrant City:\s*(.+)',
   853         'registrant_city':                'Registrant City: *(.+)',
   843         'registrant_state_province':      'Registrant State/Province:\s*(.+)',
   854         'registrant_state_province':      'Registrant State/Province: *(.+)',
   844         'registrant_postal_code':         'Registrant Postal Code:\s*(.+)',
   855         'registrant_postal_code':         'Registrant Postal Code: *(.+)',
   845         'registrant_country':             'Registrant Country:\s*(.+)',
   856         'registrant_country':             'Registrant Country: *(.+)',
   846         'registrant_country_code':        'Registrant Country Code:\s*(.+)',
   857         'registrant_country_code':        'Registrant Country Code: *(.+)',
   847         'registrant_phone_number':        'Registrant Phone Number:\s*(.+)',
   858         'registrant_phone_number':        'Registrant Phone Number: *(.+)',
   848         'registrant_email':               'Registrant Email:\s*(.+)',
   859         'registrant_email':               'Registrant Email: *(.+)',
   849         'registrant_application_purpose': 'Registrant Application Purpose:\s*(.+)',
   860         'registrant_application_purpose': 'Registrant Application Purpose: *(.+)',
   850         'registrant_nexus_category':      'Registrant Nexus Category:\s*(.+)',
   861         'registrant_nexus_category':      'Registrant Nexus Category: *(.+)',
   851         'admin_id':                       'Administrative Contact ID:\s*(.+)',
   862         'admin_id':                       'Administrative Contact ID: *(.+)',
   852         'admin_name':                     'Administrative Contact Name:\s*(.+)',
   863         'admin_name':                     'Administrative Contact Name: *(.+)',
   853         'admin_address1':                 'Administrative Contact Address1:\s*(.+)',
   864         'admin_address1':                 'Administrative Contact Address1: *(.+)',
   854         'admin_address2':                 'Administrative Contact Address2:\s*(.+)',
   865         'admin_address2':                 'Administrative Contact Address2: *(.+)',
   855         'admin_city':                     'Administrative Contact City:\s*(.+)',
   866         'admin_city':                     'Administrative Contact City: *(.+)',
   856         'admin_state_province':           'Administrative Contact State/Province:\s*(.+)',
   867         'admin_state_province':           'Administrative Contact State/Province: *(.+)',
   857         'admin_postal_code':              'Administrative Contact Postal Code:\s*(.+)',
   868         'admin_postal_code':              'Administrative Contact Postal Code: *(.+)',
   858         'admin_country':                  'Administrative Contact Country:\s*(.+)',
   869         'admin_country':                  'Administrative Contact Country: *(.+)',
   859         'admin_country_code':             'Administrative Contact Country Code:\s*(.+)',
   870         'admin_country_code':             'Administrative Contact Country Code: *(.+)',
   860         'admin_phone_number':             'Administrative Contact Phone Number:\s*(.+)',
   871         'admin_phone_number':             'Administrative Contact Phone Number: *(.+)',
   861         'admin_email':                    'Administrative Contact Email:\s*(.+)',
   872         'admin_email':                    'Administrative Contact Email: *(.+)',
   862         'admin_application_purpose':      'Administrative Application Purpose:\s*(.+)',
   873         'admin_application_purpose':      'Administrative Application Purpose: *(.+)',
   863         'admin_nexus_category':           'Administrative Nexus Category:\s*(.+)',
   874         'admin_nexus_category':           'Administrative Nexus Category: *(.+)',
   864         'billing_id':                     'Billing Contact ID:\s*(.+)',
   875         'billing_id':                     'Billing Contact ID: *(.+)',
   865         'billing_name':                   'Billing Contact Name:\s*(.+)',
   876         'billing_name':                   'Billing Contact Name: *(.+)',
   866         'billing_address1':               'Billing Contact Address1:\s*(.+)',
   877         'billing_address1':               'Billing Contact Address1: *(.+)',
   867         'billing_address2':               'Billing Contact Address2:\s*(.+)',
   878         'billing_address2':               'Billing Contact Address2: *(.+)',
   868         'billing_city':                   'Billing Contact City:\s*(.+)',
   879         'billing_city':                   'Billing Contact City: *(.+)',
   869         'billing_state_province':         'Billing Contact State/Province:\s*(.+)',
   880         'billing_state_province':         'Billing Contact State/Province: *(.+)',
   870         'billing_postal_code':            'Billing Contact Postal Code:\s*(.+)',
   881         'billing_postal_code':            'Billing Contact Postal Code: *(.+)',
   871         'billing_country':                'Billing Contact Country:\s*(.+)',
   882         'billing_country':                'Billing Contact Country: *(.+)',
   872         'billing_country_code':           'Billing Contact Country Code:\s*(.+)',
   883         'billing_country_code':           'Billing Contact Country Code: *(.+)',
   873         'billing_phone_number':           'Billing Contact Phone Number:\s*(.+)',
   884         'billing_phone_number':           'Billing Contact Phone Number: *(.+)',
   874         'billing_email':                  'Billing Contact Email:\s*(.+)',
   885         'billing_email':                  'Billing Contact Email: *(.+)',
   875         'billing_application_purpose':    'Billing Application Purpose:\s*(.+)',
   886         'billing_application_purpose':    'Billing Application Purpose: *(.+)',
   876         'billing_nexus_category':         'Billing Nexus Category:\s*(.+)',
   887         'billing_nexus_category':         'Billing Nexus Category: *(.+)',
   877         'tech_id':                        'Technical Contact ID:\s*(.+)',
   888         'tech_id':                        'Technical Contact ID: *(.+)',
   878         'tech_name':                      'Technical Contact Name:\s*(.+)',
   889         'tech_name':                      'Technical Contact Name: *(.+)',
   879         'tech_address1':                  'Technical Contact Address1:\s*(.+)',
   890         'tech_address1':                  'Technical Contact Address1: *(.+)',
   880         'tech_address2':                  'Technical Contact Address2:\s*(.+)',
   891         'tech_address2':                  'Technical Contact Address2: *(.+)',
   881         'tech_city':                      'Technical Contact City:\s*(.+)',
   892         'tech_city':                      'Technical Contact City: *(.+)',
   882         'tech_state_province':            'Technical Contact State/Province:\s*(.+)',
   893         'tech_state_province':            'Technical Contact State/Province: *(.+)',
   883         'tech_postal_code':               'Technical Contact Postal Code:\s*(.+)',
   894         'tech_postal_code':               'Technical Contact Postal Code: *(.+)',
   884         'tech_country':                   'Technical Contact Country:\s*(.+)',
   895         'tech_country':                   'Technical Contact Country: *(.+)',
   885         'tech_country_code':              'Technical Contact Country Code:\s*(.+)',
   896         'tech_country_code':              'Technical Contact Country Code: *(.+)',
   886         'tech_phone_number':              'Technical Contact Phone Number:\s*(.+)',
   897         'tech_phone_number':              'Technical Contact Phone Number: *(.+)',
   887         'tech_email':                     'Technical Contact Email:\s*(.+)',
   898         'tech_email':                     'Technical Contact Email: *(.+)',
   888         'tech_application_purpose':       'Technical Application Purpose:\s*(.+)',
   899         'tech_application_purpose':       'Technical Application Purpose: *(.+)',
   889         'tech_nexus_category':            'Technical Nexus Category:\s*(.+)',
   900         'tech_nexus_category':            'Technical Nexus Category: *(.+)',
   890         # list of name servers
   901         # list of name servers
   891         'name_servers':                   'Name Server:\s*(.+)',
   902         'name_servers':                   'Name Server: *(.+)',
   892         'created_by_registrar':           'Created by Registrar:\s*(.+)',
   903         'created_by_registrar':           'Created by Registrar: *(.+)',
   893         'last_updated_by_registrar':      'Last Updated by Registrar:\s*(.+)',
   904         'last_updated_by_registrar':      'Last Updated by Registrar: *(.+)',
   894         'creation_date':                  'Domain Registration Date:\s*(.+)',
   905         'creation_date':                  'Domain Registration Date: *(.+)',
   895         'expiration_date':                'Domain Expiration Date:\s*(.+)',
   906         'expiration_date':                'Domain Expiration Date: *(.+)',
   896         'updated_date':                   'Domain Last Updated Date:\s*(.+)',
   907         'updated_date':                   'Domain Last Updated Date: *(.+)',
   897     }
   908     }
   898 
   909 
   899     def __init__(self, domain, text):
   910     def __init__(self, domain, text):
   900         if 'Not found:' in text:
   911         if 'Not found:' in text:
   901             raise PywhoisError(text)
   912             raise PywhoisError(text)
   905 
   916 
   906 class WhoisIo(WhoisEntry):
   917 class WhoisIo(WhoisEntry):
   907     """Whois parser for .io domains
   918     """Whois parser for .io domains
   908     """
   919     """
   909     regex = {
   920     regex = {
   910         'status':           'Status\s*:\s*(.+)',
   921         'status':           'Status\s*: *(.+)',
   911         'name_servers':     'NS \d?\s*:\s*(.+)',
   922         'name_servers':     'NS \d?\s*: *(.+)',
   912         'owner':            'Owner\s*:\s*(.+)',
   923         'owner':            'Owner\s*: *(.+)',
   913         'expiration_date':  'Expiry\s*:\s*(.+)',
   924         'expiration_date':  'Expiry\s*: *(.+)',
   914         'domain_name':      'Domain\s*:\s*(.+)',
   925         'domain_name':      'Domain\s*: *(.+)',
   915         'registrar':        r'Check for \'[\w\.]*\' --- (.+)',
   926         'registrar':        r'Check for \'[\w\.]*\' --- (.+)',
   916     }
   927     }
   917 
   928 
   918     def __init__(self, domain, text):
   929     def __init__(self, domain, text):
   919         if 'is available for purchase' in text:
   930         if 'is available for purchase' in text:
   940     """Whois parser for .kg domains
   951     """Whois parser for .kg domains
   941     """
   952     """
   942     regex = {
   953     regex = {
   943         'domain_name':                    'Domain\s*([\w]+\.[\w]{2,5})',
   954         'domain_name':                    'Domain\s*([\w]+\.[\w]{2,5})',
   944         'registrar':                      'Domain support: \s*(.+)',
   955         'registrar':                      'Domain support: \s*(.+)',
   945         'registrant_name':                'Name:\s*(.+)',
   956         'registrant_name':                'Name: *(.+)',
   946         'registrant_address1':            'Address:\s*(.+)',
   957         'registrant_address1':            'Address: *(.+)',
   947         'registrant_phone_number':        'phone:\s*(.+)',
   958         'registrant_phone_number':        'phone: *(.+)',
   948         'registrant_email':               'Email:\s*(.+)',
   959         'registrant_email':               'Email: *(.+)',
   949         # # list of name servers
   960         # # list of name servers
   950         'name_servers':                   'Name servers in the listed order:\s*([\d\w\.\s]+)',
   961         'name_servers':                   'Name servers in the listed order: *([\d\w\.\s]+)',
   951         # 'name_servers':      r'([\w]+\.[\w]+\.[\w]{2,5}\s*\d{1,3}\.\d]{1,3}\.[\d]{1-3}\.[\d]{1-3})',
   962         # 'name_servers':      r'([\w]+\.[\w]+\.[\w]{2,5}\s*\d{1,3}\.\d]{1,3}\.[\d]{1-3}\.[\d]{1-3})',
   952         'creation_date':                  'Record created:\s*(.+)',
   963         'creation_date':                  'Record created: *(.+)',
   953         'expiration_date':                'Record expires on \s*(.+)',
   964         'expiration_date':                'Record expires on \s*(.+)',
   954         'updated_date':                   'Record last updated on\s*(.+)',
   965         'updated_date':                   'Record last updated on\s*(.+)',
   955 
   966 
   956     }
   967     }
   957     def __init__(self, domain, text):
   968     def __init__(self, domain, text):