# -*- coding: UTF-8 -*- ''' @Project :domainScanDemo @File :domain_collector.py @IDE :PyCharm @Author :梦伴 @Date :2026/4/8 23:50 @explain : 域名收集器 ''' import re import tldextract from loguru import logger from app.utils.database import Database from app.utils.domain_utils import normalize_domain from app.config import config class DomainCollector: """ 域名收集器 """ def __init__(self): """ 初始化域名收集器 """ self.db = Database( host=config.DB_HOST, port=config.DB_PORT, database=config.DB_DATABASE, user=config.DB_USER, password=config.DB_PASSWORD ) def add_domain(self, domain, source_type): """ 添加域名 :param domain: 域名 :param source_type: 来源类型 :return: bool - 是否添加成功 """ try: # 标准化域名 normalized_domain = normalize_domain(domain) if not normalized_domain: logger.warning(f"无效域名: {domain}") return False # 提取顶级域名 ext = tldextract.extract(normalized_domain) tld = ext.suffix # 只保留 .com 和 .net if tld not in ['com', 'net']: logger.warning(f"不支持的顶级域名: {tld}") return False # 检查是否已存在 if self.db.domain_exists(normalized_domain): logger.info(f"域名已存在: {normalized_domain}") return False # 添加域名 domain_id = self.db.add_domain(normalized_domain, tld, source_type) if domain_id: # 创建检测任务 self.db.create_detect_task(domain_id, 1) # 1 表示基础检测 logger.info(f"成功添加域名: {normalized_domain}") return True else: logger.error(f"添加域名失败: {normalized_domain}") return False except Exception as e: logger.error(f"添加域名出错: {e}") return False def add_domains_batch(self, domains, source_type, batch_size=1000, dry_run=False): """ 批量添加域名 :param domains: 域名列表 :param source_type: 来源类型 :param batch_size: 批量大小 :param dry_run: 是否仅进行干运行(不实际添加域名) :return: dict - 统计信息 """ try: # 统计信息 stats = { 'total': len(domains), 'valid': 0, 'added': 0, 'exists': 0, 'invalid': 0, 'failed': 0 } # 处理大规模数据时,分批进行标准化和过滤 normalized_domains = [] batch_domains = [] for i, domain in enumerate(domains): normalized = normalize_domain(domain) if not normalized: stats['invalid'] += 1 continue # 提取顶级域名 ext = tldextract.extract(normalized) tld = ext.suffix # 只保留 .com 和 .net if tld not in ['com', 'net']: stats['invalid'] += 1 continue normalized_domains.append((normalized, tld)) batch_domains.append(normalized) stats['valid'] += 1 # 每1000个域名检查一次,避免内存占用过高 if (i + 1) % 1000 == 0: logger.info(f"已处理 {i + 1}/{len(domains)} 个域名") logger.info(f"域名标准化完成,有效域名: {stats['valid']}") # 提取所有域名 all_domains = [domain for domain, tld in normalized_domains] # 批量检查域名是否存在 existing_domains = self.db.check_domains_exist(all_domains) existing_set = set(existing_domains) # 准备批量添加数据 batch_data = [] for domain, tld in normalized_domains: if domain not in existing_set: batch_data.append((domain, tld, source_type)) stats['exists'] = len(existing_domains) stats['valid'] = len(normalized_domains) # 干运行模式下直接返回统计信息 if dry_run: stats['added'] = len(batch_data) logger.info(f"干运行模式:准备添加 {len(batch_data)} 个新域名") return stats logger.info(f"准备添加 {len(batch_data)} 个新域名") # 分批次添加 for i in range(0, len(batch_data), batch_size): batch = batch_data[i:i+batch_size] added_count = self.db.add_domains_batch(batch) stats['added'] += added_count stats['failed'] += len(batch) - added_count # 每处理一批,记录一次进度 if (i + len(batch)) % (batch_size * 10) == 0: logger.info(f"已添加 {i + len(batch)}/{len(batch_data)} 个域名") logger.info(f"批量添加域名完成: {stats}") return stats except Exception as e: logger.error(f"批量添加域名出错: {e}") return { 'total': len(domains), 'valid': 0, 'added': 0, 'exists': 0, 'invalid': 0, 'failed': len(domains) } def import_from_file(self, file_path, source_type, batch_size=1000): """ 从文件导入域名 :param file_path: 文件路径 :param source_type: 来源类型 :param batch_size: 批量大小 :return: dict - 导入统计信息 """ try: with open(file_path, 'r', encoding='utf-8') as f: domains = f.readlines() # 提取域名 domain_list = [] for domain in domains: domain = domain.strip() if domain: domain_list.append(domain) # 批量添加域名 stats = self.add_domains_batch(domain_list, source_type, batch_size) logger.info(f"从文件导入完成: {stats}") return stats except Exception as e: logger.error(f"从文件导入出错: {e}") return { 'total': 0, 'valid': 0, 'added': 0, 'exists': 0, 'invalid': 0, 'failed': 0 } def collect_from_juming(self, type_='一口价'): """ 从聚名网收集域名 :param type_: 类型,一口价或过期删除 :return: int - 收集到的域名数量 """ # 这里可以添加从聚名网收集域名的逻辑 logger.info(f"从聚名网收集 {type_} 域名") # 模拟收集结果 return 0 def collect_from_search_engine(self, keyword, limit=100): """ 从搜索引擎收集域名 :param keyword: 关键词 :param limit: 限制数量 :return: int - 收集到的域名数量 """ # 这里可以添加从搜索引擎收集域名的逻辑 logger.info(f"从搜索引擎收集域名,关键词: {keyword}, 限制: {limit}") # 模拟收集结果 return 0 def collect_from_enterprise_directory(self, url, limit=100): """ 从企业目录收集域名 :param url: 企业目录URL :param limit: 限制数量 :return: int - 收集到的域名数量 """ # 这里可以添加从企业目录收集域名的逻辑 logger.info(f"从企业目录收集域名,URL: {url}, 限制: {limit}") # 模拟收集结果 return 0 def collect_from_zone_file(self, file_path): """ 从Zone File收集域名 :param file_path: Zone File路径 :return: int - 收集到的域名数量 """ # 这里可以添加从Zone File收集域名的逻辑 logger.info(f"从Zone File收集域名,文件: {file_path}") # 模拟收集结果 return 0