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