searchdomain.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import whois
  2. from concurrent.futures import ThreadPoolExecutor
  3. import logging,os
  4. import argparse
  5. from . import db
  6. class SearchDomain(object):
  7. """search avaliable domain and save result"""
  8. def __init__(self, params: dict, debug=False, export_all=True, log_callback=None):
  9. '''
  10. 初始化
  11. debug 调试模式
  12. export_all 是否导出所有域名,默认导出可用域名
  13. log_callback 日志回调函数,用于GUI模式下显示日志
  14. return:
  15. '''
  16. super(SearchDomain, self).__init__()
  17. self.params = params
  18. self.export_all=export_all
  19. self.input=params["input"]
  20. self.output=params["output"]
  21. self.log_callback = log_callback
  22. self._output_file_checked = False # 标记输出文件是否已检查
  23. # 获取当前模块的日志记录器
  24. self.logger = logging.getLogger(__name__)
  25. # 配置日志系统
  26. if log_callback:
  27. # 如果有日志回调函数,创建自定义处理器
  28. # 创建自定义处理器,将日志输出到回调函数
  29. class CallbackHandler(logging.Handler):
  30. def __init__(self, callback):
  31. super().__init__()
  32. self.callback = callback
  33. def emit(self, record):
  34. msg = self.format(record)
  35. if self.callback:
  36. self.callback(msg)
  37. self.logger.setLevel(logging.INFO)
  38. # 检查是否已经添加了回调处理器,避免重复添加
  39. has_callback_handler = any(isinstance(h, CallbackHandler) for h in self.logger.handlers)
  40. if not has_callback_handler:
  41. callback_handler = CallbackHandler(log_callback)
  42. callback_handler.setLevel(logging.INFO)
  43. formatter = logging.Formatter('%(message)s')
  44. callback_handler.setFormatter(formatter)
  45. self.logger.addHandler(callback_handler)
  46. elif debug == True:
  47. logging.basicConfig(level=logging.DEBUG, format='%(message)s', force=True)
  48. else:
  49. logging.basicConfig(level=logging.INFO, format='%(message)s', force=True)
  50. def crawl(self, domain: str, index:int) -> None:
  51. '''
  52. 检测域名是否可用
  53. :params domain 域名:
  54. :return true or false'''
  55. res = False
  56. try:
  57. whi = whois.whois(domain)
  58. res = False
  59. self.logger.info(str(index) + ": searching domain:"+ domain + " is unavaliable.")
  60. except Exception as e:
  61. error_str = str(e)
  62. # 检查是否是域名未注册的错误
  63. if "No match" in error_str or "No match for" in error_str:
  64. res = True
  65. self.logger.info(str(index) + ": searching domain:"+ domain +" is avaliable.")
  66. else:
  67. res = False
  68. self.logger.error(f"Error checking {domain}: {error_str}")
  69. if self.export_all:
  70. self.saveRes(domain, res)
  71. else:
  72. if res:
  73. self.saveRes(domain, res)
  74. def saveRes(self, domain: str, res: bool):
  75. """ save result to file """
  76. # db.Mysql().save()
  77. output_path = os.path.join(self.params["app_path"], self.output)
  78. # 检查输出文件是否存在,不存在则创建并警告(只检查一次)
  79. if not self._output_file_checked:
  80. if not os.path.exists(output_path):
  81. # 确保目录存在
  82. dir_path = os.path.dirname(output_path)
  83. if dir_path and not os.path.exists(dir_path):
  84. os.makedirs(dir_path, exist_ok=True)
  85. # 创建文件
  86. with open(output_path, 'w', encoding='utf-8') as f:
  87. pass
  88. self.logger.warning(f"警告:输出文件不存在,已创建: {output_path}")
  89. self._output_file_checked = True
  90. db.File().save(output_path, domain + " " + str(res))
  91. def run(self):
  92. '''begin search domain'''
  93. # 支持 input 为完整路径或相对路径
  94. input_path = self.input if os.path.isabs(self.input) else os.path.join(self.params["app_path"], self.input)
  95. with open(input_path, "r", encoding="utf8", errors="ignore") as file:
  96. with ThreadPoolExecutor(max_workers=5) as pool:
  97. index = 0
  98. futures = []
  99. for line in file.readlines():
  100. domain = line.strip()
  101. if domain: # 跳过空行
  102. index = index + 1
  103. future = pool.submit(self.crawl, domain, index)
  104. futures.append(future)
  105. # 等待所有任务完成
  106. for future in futures:
  107. future.result()
  108. if __name__ == '__main__':
  109. sd = SearchDomain()
  110. sd.run()