convert domainCheck to regular directory
This commit is contained in:
4
domainCheck/app/core/__init__.py
Normal file
4
domainCheck/app/core/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
'''
|
||||
核心功能模块
|
||||
'''
|
||||
286
domainCheck/app/core/detect_engine.py
Normal file
286
domainCheck/app/core/detect_engine.py
Normal file
@@ -0,0 +1,286 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
'''
|
||||
@Project :domainScanDemo
|
||||
@File :detect_engine.py
|
||||
@IDE :PyCharm
|
||||
@Author :梦伴
|
||||
@Date :2026/4/8 23:52
|
||||
@explain : 检测引擎
|
||||
'''
|
||||
|
||||
import time
|
||||
from loguru import logger
|
||||
from app.utils.database import Database
|
||||
from app.detectors.rdap_detector import RDAPDetector
|
||||
from app.detectors.wayback_detector import WaybackDetector
|
||||
from app.detectors.baidu_detector import BaiduDetector
|
||||
from app.detectors.qihu360_detector import Qihu360Detector
|
||||
from app.detectors.google_detector import GoogleDetector
|
||||
from app.detectors.chinaz_detector import ChinazDetector
|
||||
from app.detectors.aizhan_detector import AizhanDetector
|
||||
from app.detectors.juziseo_detector import JuziseoDetector
|
||||
from app.detectors.jucha_detector import JuchaDetector
|
||||
from app.utils.status_codes import (
|
||||
DETECT_STATUS_BLACKLISTED,
|
||||
DETECT_STATUS_COMPLETED,
|
||||
DETECT_STATUS_FAILED,
|
||||
DETECT_STATUS_RUNNING,
|
||||
)
|
||||
|
||||
|
||||
class DetectEngine:
|
||||
"""
|
||||
检测引擎
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化检测引擎
|
||||
"""
|
||||
self.db = Database()
|
||||
self.rdap_detector = RDAPDetector()
|
||||
self.wayback_detector = WaybackDetector()
|
||||
self.baidu_detector = BaiduDetector()
|
||||
self.qihu360_detector = Qihu360Detector()
|
||||
self.google_detector = GoogleDetector()
|
||||
self.chinaz_detector = ChinazDetector()
|
||||
self.aizhan_detector = AizhanDetector()
|
||||
self.juziseo_detector = JuziseoDetector()
|
||||
self.jucha_detector = JuchaDetector()
|
||||
|
||||
def detect_domain(self, domain_id):
|
||||
"""
|
||||
检测域名
|
||||
|
||||
:param domain_id: 域名ID
|
||||
:return: bool - 是否检测成功
|
||||
"""
|
||||
try:
|
||||
# 获取域名信息
|
||||
domain_info = self.db.get_domain_by_id(domain_id)
|
||||
if not domain_info:
|
||||
logger.error(f"域名不存在: {domain_id}")
|
||||
return False
|
||||
|
||||
domain = domain_info['domain']
|
||||
logger.info(f"开始检测域名: {domain}")
|
||||
|
||||
# 更新检测状态为检测中
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_RUNNING)
|
||||
|
||||
# 1. 基础检测
|
||||
if not self._basic_detect(domain_id, domain):
|
||||
logger.info(f"基础检测失败,停止后续检测: {domain}")
|
||||
return False
|
||||
|
||||
# 2. 深度检测
|
||||
if not self._deep_detect(domain_id, domain):
|
||||
logger.info(f"深度检测失败: {domain}")
|
||||
return False
|
||||
|
||||
# 更新检测状态为正常
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_COMPLETED)
|
||||
logger.info(f"域名检测完成: {domain}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"检测域名出错: {e}")
|
||||
# 更新检测状态为检测失败
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_FAILED)
|
||||
return False
|
||||
|
||||
def _basic_detect(self, domain_id, domain):
|
||||
"""
|
||||
基础检测
|
||||
|
||||
:param domain_id: 域名ID
|
||||
:param domain: 域名
|
||||
:return: bool - 是否检测通过
|
||||
"""
|
||||
# 1. 检查是否为一口价域名
|
||||
is_ykj = self.db.is_ykj_domain(domain_id)
|
||||
|
||||
# 2. 注册状态检测(一口价域名跳过)
|
||||
if not is_ykj:
|
||||
register_status = self.rdap_detector.check_register_status(domain)
|
||||
self.db.update_domain_register_status(domain_id, register_status)
|
||||
|
||||
# 3. 黑名单缓存检查
|
||||
if self.db.is_blacklisted(domain):
|
||||
logger.info(f"域名在黑名单中: {domain}")
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_BLACKLISTED)
|
||||
return False
|
||||
|
||||
# 4. 时光机快照年份采集
|
||||
snapshot_years = self.wayback_detector.get_snapshot_years(domain)
|
||||
if snapshot_years:
|
||||
self.db.update_domain_snapshot_years(domain_id, ','.join(map(str, snapshot_years)))
|
||||
|
||||
# 5. 时光机正文抽样与敏感词匹配
|
||||
if self.wayback_detector.has_sensitive_content(domain):
|
||||
logger.info(f"域名包含敏感词: {domain}")
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_BLACKLISTED)
|
||||
self.db.add_to_blacklist(domain, "快照包含敏感词")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _deep_detect(self, domain_id, domain):
|
||||
"""
|
||||
深度检测
|
||||
|
||||
:param domain_id: 域名ID
|
||||
:param domain: 域名
|
||||
:return: bool - 是否检测通过
|
||||
"""
|
||||
# 1. 百度历史/Site
|
||||
baidu_history = self.baidu_detector.check_history(domain)
|
||||
baidu_site = self.baidu_detector.check_site(domain)
|
||||
|
||||
# 2. 360 Site
|
||||
qihu360_site = self.qihu360_detector.check_site(domain)
|
||||
|
||||
# 3. Google Site
|
||||
google_site = self.google_detector.check_site(domain)
|
||||
|
||||
# 4. 站长之家
|
||||
chinaz_info = self.chinaz_detector.check_domain(domain)
|
||||
|
||||
# 5. 爱站网
|
||||
aizhan_info = self.aizhan_detector.check_domain(domain)
|
||||
|
||||
# 6. 桔子SEO
|
||||
juziseo_info = self.juziseo_detector.check_domain(domain)
|
||||
|
||||
# 7. 聚查
|
||||
jucha_info = self.jucha_detector.check_domain(domain)
|
||||
|
||||
# 检查是否有风险
|
||||
if self._check_risk(domain_id, domain, baidu_history, baidu_site, qihu360_site, google_site, chinaz_info, aizhan_info, juziseo_info, jucha_info):
|
||||
return False
|
||||
|
||||
# 保存检测结果
|
||||
self.db.add_detection_result(domain_id, baidu_history, baidu_site, qihu360_site, google_site, chinaz_info, aizhan_info, juziseo_info, jucha_info)
|
||||
|
||||
return True
|
||||
|
||||
def _check_risk(self, domain_id, domain, baidu_history, baidu_site, qihu360_site, google_site, chinaz_info, aizhan_info, juziseo_info, jucha_info):
|
||||
"""
|
||||
检查风险
|
||||
|
||||
:param domain_id: 域名ID
|
||||
:param domain: 域名
|
||||
:param baidu_history: 百度历史
|
||||
:param baidu_site: 百度site
|
||||
:param qihu360_site: 360 site
|
||||
:param google_site: Google site
|
||||
:param chinaz_info: 站长之家信息
|
||||
:param aizhan_info: 爱站网信息
|
||||
:param juziseo_info: 桔子SEO信息
|
||||
:param jucha_info: 聚查信息
|
||||
:return: bool - 是否有风险
|
||||
"""
|
||||
# 检查百度历史过灰
|
||||
if baidu_history and '灰' in str(baidu_history):
|
||||
logger.info(f"百度历史过灰: {domain}")
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_BLACKLISTED)
|
||||
self.db.add_to_blacklist(domain, "百度历史过灰")
|
||||
return True
|
||||
|
||||
# 检查标题敏感词
|
||||
if chinaz_info and 'title' in chinaz_info:
|
||||
if self._contains_sensitive_words(chinaz_info['title']):
|
||||
logger.info(f"标题包含敏感词: {domain}")
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_BLACKLISTED)
|
||||
self.db.add_to_blacklist(domain, "标题包含敏感词")
|
||||
return True
|
||||
|
||||
# 检查子域名
|
||||
if baidu_site and 'subdomains' in baidu_site:
|
||||
subdomains = baidu_site['subdomains']
|
||||
# 排除 www, @, m
|
||||
valid_subdomains = [sub for sub in subdomains if sub not in ['www', '@', 'm']]
|
||||
if valid_subdomains:
|
||||
logger.info(f"存在子域名: {domain}")
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_BLACKLISTED)
|
||||
self.db.add_to_blacklist(domain, "存在子域名")
|
||||
return True
|
||||
|
||||
# 检查风险提示
|
||||
if aizhan_info and 'risk' in aizhan_info:
|
||||
if aizhan_info['risk'] in ['低风险', '疑似色情博彩风险', '严重影响权重']:
|
||||
logger.info(f"风险提示: {domain}")
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_BLACKLISTED)
|
||||
self.db.add_to_blacklist(domain, f"风险提示: {aizhan_info['risk']}")
|
||||
return True
|
||||
|
||||
# 检查WHOIS状态
|
||||
if jucha_info and 'whois' in jucha_info:
|
||||
if jucha_info['whois'].get('status') in ['clientHold', 'serverHold']:
|
||||
logger.info(f"WHOIS状态异常: {domain}")
|
||||
self.db.update_domain_detect_status(domain_id, DETECT_STATUS_BLACKLISTED)
|
||||
self.db.add_to_blacklist(domain, "WHOIS状态异常")
|
||||
return True
|
||||
|
||||
# 检查拦截检测
|
||||
if jucha_info and 'intercept' in jucha_info:
|
||||
if not jucha_info['intercept'].get('normal', True):
|
||||
logger.info(f"拦截检测异常: {domain}")
|
||||
self.db.update_domain_detect_status(domain_id, 4) # 4 表示黑名单
|
||||
self.db.add_to_blacklist(domain, "拦截检测异常")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _contains_sensitive_words(self, text):
|
||||
"""
|
||||
检查文本是否包含敏感词
|
||||
|
||||
:param text: 文本
|
||||
:return: bool - 是否包含敏感词
|
||||
"""
|
||||
sensitive_words = self.db.get_all_sensitive_words()
|
||||
for word in sensitive_words:
|
||||
if word in text:
|
||||
return True
|
||||
return False
|
||||
|
||||
def process_task(self, task_id):
|
||||
"""
|
||||
处理检测任务
|
||||
|
||||
:param task_id: 任务ID
|
||||
:return: bool - 是否处理成功
|
||||
"""
|
||||
try:
|
||||
# 获取任务信息
|
||||
task = self.db.get_task_by_id(task_id)
|
||||
if not task:
|
||||
logger.error(f"任务不存在: {task_id}")
|
||||
return False
|
||||
|
||||
domain_id = task['domain_id']
|
||||
|
||||
# 更新任务状态为执行中
|
||||
self.db.update_task_status(task_id, 1) # 1 表示执行中
|
||||
|
||||
# 执行检测
|
||||
success = self.detect_domain(domain_id)
|
||||
|
||||
# 更新任务状态
|
||||
if success:
|
||||
self.db.update_task_status(task_id, 2) # 2 表示完成
|
||||
else:
|
||||
# 增加重试次数
|
||||
retry_count = task.get('retry_count', 0) + 1
|
||||
if retry_count < 3:
|
||||
self.db.update_task_retry_count(task_id, retry_count)
|
||||
self.db.update_task_status(task_id, 0) # 0 表示待执行
|
||||
else:
|
||||
self.db.update_task_status(task_id, 3) # 3 表示失败
|
||||
|
||||
return success
|
||||
except Exception as e:
|
||||
logger.error(f"处理任务出错: {e}")
|
||||
# 更新任务状态为失败
|
||||
self.db.update_task_status(task_id, 3) # 3 表示失败
|
||||
return False
|
||||
261
domainCheck/app/core/domain_collector.py
Normal file
261
domainCheck/app/core/domain_collector.py
Normal file
@@ -0,0 +1,261 @@
|
||||
# -*- 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
|
||||
150
domainCheck/app/core/domain_processor.py
Normal file
150
domainCheck/app/core/domain_processor.py
Normal file
@@ -0,0 +1,150 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
'''
|
||||
@Project :domainScanDemo
|
||||
@File :domain_processor.py
|
||||
@IDE :PyCharm
|
||||
@Author :梦伴
|
||||
@Date :2026/4/8 23:51
|
||||
@explain : 域名处理器
|
||||
'''
|
||||
|
||||
import re
|
||||
import tldextract
|
||||
from loguru import logger
|
||||
from app.utils.database import Database
|
||||
from app.utils.domain_utils import normalize_domain
|
||||
|
||||
|
||||
class DomainProcessor:
|
||||
"""
|
||||
域名处理器
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化域名处理器
|
||||
"""
|
||||
self.db = Database()
|
||||
|
||||
def process_domain(self, domain):
|
||||
"""
|
||||
处理域名
|
||||
|
||||
:param domain: 域名
|
||||
:return: dict - 处理结果
|
||||
"""
|
||||
result = {
|
||||
'original': domain,
|
||||
'normalized': None,
|
||||
'tld': None,
|
||||
'valid': False,
|
||||
'reason': ''
|
||||
}
|
||||
|
||||
try:
|
||||
# 标准化域名
|
||||
normalized = normalize_domain(domain)
|
||||
if not normalized:
|
||||
result['reason'] = '无效域名格式'
|
||||
return result
|
||||
|
||||
# 提取顶级域名
|
||||
ext = tldextract.extract(normalized)
|
||||
tld = ext.suffix
|
||||
|
||||
# 检查顶级域名
|
||||
if tld not in ['com', 'net']:
|
||||
result['reason'] = '不支持的顶级域名'
|
||||
return result
|
||||
|
||||
result['normalized'] = normalized
|
||||
result['tld'] = tld
|
||||
result['valid'] = True
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f"处理域名出错: {e}")
|
||||
result['reason'] = f"处理出错: {str(e)}"
|
||||
return result
|
||||
|
||||
def batch_process(self, domains):
|
||||
"""
|
||||
批量处理域名
|
||||
|
||||
:param domains: 域名列表
|
||||
:return: list - 处理结果列表
|
||||
"""
|
||||
results = []
|
||||
for domain in domains:
|
||||
result = self.process_domain(domain)
|
||||
results.append(result)
|
||||
return results
|
||||
|
||||
def filter_valid_domains(self, domains):
|
||||
"""
|
||||
过滤有效的域名
|
||||
|
||||
:param domains: 域名列表
|
||||
:return: list - 有效的域名列表
|
||||
"""
|
||||
valid_domains = []
|
||||
for domain in domains:
|
||||
result = self.process_domain(domain)
|
||||
if result['valid']:
|
||||
valid_domains.append(result['normalized'])
|
||||
return valid_domains
|
||||
|
||||
def update_domain_status(self, domain_id, status_type, status_value):
|
||||
"""
|
||||
更新域名状态
|
||||
|
||||
:param domain_id: 域名ID
|
||||
:param status_type: 状态类型
|
||||
:param status_value: 状态值
|
||||
:return: bool - 是否更新成功
|
||||
"""
|
||||
try:
|
||||
if status_type == 'use_status':
|
||||
return self.db.update_domain_use_status(domain_id, status_value)
|
||||
elif status_type == 'detect_status':
|
||||
return self.db.update_domain_detect_status(domain_id, status_value)
|
||||
elif status_type == 'register_status':
|
||||
return self.db.update_domain_register_status(domain_id, status_value)
|
||||
else:
|
||||
logger.error(f"未知的状态类型: {status_type}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"更新域名状态出错: {e}")
|
||||
return False
|
||||
|
||||
def blacklist_domain(self, domain_id, reason):
|
||||
"""
|
||||
将域名加入黑名单
|
||||
|
||||
:param domain_id: 域名ID
|
||||
:param reason: 黑名单原因
|
||||
:return: bool - 是否操作成功
|
||||
"""
|
||||
try:
|
||||
# 更新检测状态为黑名单
|
||||
if self.db.update_domain_detect_status(domain_id, 4): # 4 表示黑名单
|
||||
# 添加到黑名单表
|
||||
domain = self.db.get_domain_by_id(domain_id)
|
||||
if domain:
|
||||
return self.db.add_to_blacklist(domain['domain'], reason)
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"将域名加入黑名单出错: {e}")
|
||||
return False
|
||||
|
||||
def get_domain_statistics(self):
|
||||
"""
|
||||
获取域名统计信息
|
||||
|
||||
:return: dict - 统计信息
|
||||
"""
|
||||
try:
|
||||
return self.db.get_domain_statistics()
|
||||
except Exception as e:
|
||||
logger.error(f"获取域名统计信息出错: {e}")
|
||||
return {}
|
||||
289
domainCheck/app/core/export_manager.py
Normal file
289
domainCheck/app/core/export_manager.py
Normal file
@@ -0,0 +1,289 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
'''
|
||||
@Project :domainScanDemo
|
||||
@File :export_manager.py
|
||||
@IDE :PyCharm
|
||||
@Author :梦伴
|
||||
@Date :2026/4/8 23:54
|
||||
@explain : 导出管理器
|
||||
'''
|
||||
|
||||
import os
|
||||
from loguru import logger
|
||||
from app.utils.database import Database
|
||||
|
||||
|
||||
class ExportManager:
|
||||
"""
|
||||
导出管理器
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化导出管理器
|
||||
"""
|
||||
self.db = Database()
|
||||
|
||||
def export_domains(self, filter_conditions, output_file):
|
||||
"""
|
||||
导出域名
|
||||
|
||||
:param filter_conditions: 筛选条件
|
||||
:param output_file: 输出文件路径
|
||||
:return: int - 导出的域名数量
|
||||
"""
|
||||
try:
|
||||
# 查询符合条件的域名
|
||||
domains = self.db.get_domains_by_conditions(filter_conditions)
|
||||
|
||||
if not domains:
|
||||
logger.warning("没有符合条件的域名")
|
||||
return 0
|
||||
|
||||
# 导出到文件
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
for domain in domains:
|
||||
f.write(domain['domain'] + '\n')
|
||||
|
||||
logger.info(f"成功导出 {len(domains)} 个域名到 {output_file}")
|
||||
return len(domains)
|
||||
except Exception as e:
|
||||
logger.error(f"导出域名出错: {e}")
|
||||
return 0
|
||||
|
||||
def export_with_details(self, filter_conditions, output_file):
|
||||
"""
|
||||
导出域名及其详细信息
|
||||
|
||||
:param filter_conditions: 筛选条件
|
||||
:param output_file: 输出文件路径
|
||||
:return: int - 导出的域名数量
|
||||
"""
|
||||
try:
|
||||
# 查询符合条件的域名及其详细信息
|
||||
domains = self.db.get_domains_with_details(filter_conditions)
|
||||
|
||||
if not domains:
|
||||
logger.warning("没有符合条件的域名")
|
||||
return 0
|
||||
|
||||
# 导出到文件
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
# 写入表头
|
||||
f.write('域名,注册状态,使用状态,检测状态,备案历史,备案年份,快照年份,友情链接数量\n')
|
||||
|
||||
# 写入数据
|
||||
for domain in domains:
|
||||
line = f"{domain['domain']},{domain['register_status']},{domain['use_status']},{domain['detect_status']},{domain['has_beian']},{domain['beian_year']},{domain['snapshot_years']},{domain['backlink_count']}\n"
|
||||
f.write(line)
|
||||
|
||||
logger.info(f"成功导出 {len(domains)} 个域名及其详细信息到 {output_file}")
|
||||
return len(domains)
|
||||
except Exception as e:
|
||||
logger.error(f"导出域名详细信息出错: {e}")
|
||||
return 0
|
||||
|
||||
def batch_update_status(self, domain_ids, status_type, status_value):
|
||||
"""
|
||||
批量更新域名状态
|
||||
|
||||
:param domain_ids: 域名ID列表
|
||||
:param status_type: 状态类型
|
||||
:param status_value: 状态值
|
||||
:return: int - 更新成功的域名数量
|
||||
"""
|
||||
try:
|
||||
success_count = 0
|
||||
for domain_id in domain_ids:
|
||||
if self.db.update_domain_status(domain_id, status_type, status_value):
|
||||
success_count += 1
|
||||
|
||||
logger.info(f"成功更新 {success_count} 个域名的状态")
|
||||
return success_count
|
||||
except Exception as e:
|
||||
logger.error(f"批量更新域名状态出错: {e}")
|
||||
return 0
|
||||
|
||||
def get_filtered_domains(self, filter_conditions, limit=1000):
|
||||
"""
|
||||
获取符合条件的域名
|
||||
|
||||
:param filter_conditions: 筛选条件
|
||||
:param limit: 限制数量
|
||||
:return: list - 域名列表
|
||||
"""
|
||||
try:
|
||||
domains = self.db.get_domains_by_conditions(filter_conditions, limit)
|
||||
logger.info(f"获取到 {len(domains)} 个符合条件的域名")
|
||||
return domains
|
||||
except Exception as e:
|
||||
logger.error(f"获取符合条件的域名出错: {e}")
|
||||
return []
|
||||
|
||||
def generate_report(self, output_file):
|
||||
"""
|
||||
生成统计报告
|
||||
|
||||
:param output_file: 输出文件路径
|
||||
:return: bool - 是否生成成功
|
||||
"""
|
||||
try:
|
||||
# 获取域名统计信息
|
||||
domain_stats = self.db.get_domain_statistics()
|
||||
|
||||
# 获取任务统计信息
|
||||
task_stats = self.db.get_task_statistics()
|
||||
|
||||
# 生成报告
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
f.write('域名库系统统计报告\n')
|
||||
f.write('=' * 50 + '\n')
|
||||
|
||||
f.write('\n域名统计:\n')
|
||||
f.write(f'总域名数: {domain_stats.get("total", 0)}\n')
|
||||
f.write(f'可注册域名: {domain_stats.get("available", 0)}\n')
|
||||
f.write(f'已注册域名: {domain_stats.get("registered", 0)}\n')
|
||||
f.write(f'黑名单域名: {domain_stats.get("blacklisted", 0)}\n')
|
||||
|
||||
f.write('\n任务统计:\n')
|
||||
f.write(f'总任务数: {task_stats.get("total", 0)}\n')
|
||||
f.write(f'待执行任务: {task_stats.get("pending", 0)}\n')
|
||||
f.write(f'执行中任务: {task_stats.get("running", 0)}\n')
|
||||
f.write(f'已完成任务: {task_stats.get("completed", 0)}\n')
|
||||
f.write(f'失败任务: {task_stats.get("failed", 0)}\n')
|
||||
|
||||
logger.info(f"成功生成统计报告到 {output_file}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"生成统计报告出错: {e}")
|
||||
return False
|
||||
|
||||
def export_to_excel(self, domains, output_file):
|
||||
"""
|
||||
导出域名到Excel文件
|
||||
|
||||
:param domains: 域名列表
|
||||
:param output_file: 输出文件路径
|
||||
:return: int - 导出的域名数量
|
||||
"""
|
||||
try:
|
||||
# 尝试导入openpyxl
|
||||
try:
|
||||
from openpyxl import Workbook
|
||||
except ImportError:
|
||||
# 如果没有安装openpyxl,使用CSV格式作为替代
|
||||
logger.warning("openpyxl库未安装,将使用CSV格式导出")
|
||||
return self.export_to_csv(domains, output_file.replace('.xlsx', '.csv'))
|
||||
|
||||
# 创建工作簿
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
|
||||
# 写入表头
|
||||
headers = ['域名', '注册状态', '使用状态', '检测状态', '人工复核状态', '过期时间', '单位性质', '网站首页网址', '检测时间', '备案历史', '备案年份', '快照年份', '百度历史', '百度Site', '是否中文标题', '360 Site', 'Google Site', '友情链接数量']
|
||||
ws.append(headers)
|
||||
|
||||
# 写入数据
|
||||
for domain in domains:
|
||||
row = [
|
||||
domain.get('domain', ''),
|
||||
domain.get('register_status', ''),
|
||||
domain.get('use_status', ''),
|
||||
domain.get('detect_status', ''),
|
||||
domain.get('review_status', ''),
|
||||
domain.get('expire_date', ''),
|
||||
domain.get('company_type', ''),
|
||||
domain.get('website_url', ''),
|
||||
domain.get('detect_time', ''),
|
||||
domain.get('has_beian', ''),
|
||||
domain.get('beian_year', ''),
|
||||
domain.get('snapshot_years', ''),
|
||||
domain.get('baidu_history', ''),
|
||||
domain.get('baidu_site', ''),
|
||||
domain.get('is_chinese_title', ''),
|
||||
domain.get('qihu360_site', ''),
|
||||
domain.get('google_site', ''),
|
||||
domain.get('backlink_count', '')
|
||||
]
|
||||
ws.append(row)
|
||||
|
||||
# 保存文件
|
||||
wb.save(output_file)
|
||||
|
||||
logger.info(f"成功导出 {len(domains)} 个域名到Excel文件: {output_file}")
|
||||
return len(domains)
|
||||
except Exception as e:
|
||||
logger.error(f"导出Excel文件出错: {e}")
|
||||
# 尝试使用CSV格式作为替代
|
||||
try:
|
||||
csv_file = output_file.replace('.xlsx', '.csv')
|
||||
logger.info(f"尝试使用CSV格式导出到: {csv_file}")
|
||||
return self.export_to_csv(domains, csv_file)
|
||||
except Exception as e2:
|
||||
logger.error(f"导出CSV文件也失败: {e2}")
|
||||
raise
|
||||
|
||||
def export_to_csv(self, domains, output_file):
|
||||
"""
|
||||
导出域名到CSV文件
|
||||
|
||||
:param domains: 域名列表
|
||||
:param output_file: 输出文件路径
|
||||
:return: int - 导出的域名数量
|
||||
"""
|
||||
try:
|
||||
import csv
|
||||
|
||||
# 写入文件
|
||||
with open(output_file, 'w', encoding='utf-8', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
|
||||
# 写入表头
|
||||
headers = ['域名', '注册状态', '使用状态', '检测状态', '人工复核状态', '过期时间', '单位性质', '网站首页网址', '检测时间', '备案历史', '备案年份', '快照年份', '百度历史', '百度Site', '是否中文标题', '360 Site', 'Google Site', '友情链接数量']
|
||||
writer.writerow(headers)
|
||||
|
||||
# 写入数据
|
||||
for domain in domains:
|
||||
row = [
|
||||
domain.get('domain', ''),
|
||||
domain.get('register_status', ''),
|
||||
domain.get('use_status', ''),
|
||||
domain.get('detect_status', ''),
|
||||
domain.get('review_status', ''),
|
||||
domain.get('expire_date', ''),
|
||||
domain.get('company_type', ''),
|
||||
domain.get('website_url', ''),
|
||||
domain.get('detect_time', ''),
|
||||
domain.get('has_beian', ''),
|
||||
domain.get('beian_year', ''),
|
||||
domain.get('snapshot_years', ''),
|
||||
domain.get('baidu_history', ''),
|
||||
domain.get('baidu_site', ''),
|
||||
domain.get('is_chinese_title', ''),
|
||||
domain.get('qihu360_site', ''),
|
||||
domain.get('google_site', ''),
|
||||
domain.get('backlink_count', '')
|
||||
]
|
||||
writer.writerow(row)
|
||||
|
||||
logger.info(f"成功导出 {len(domains)} 个域名到CSV文件: {output_file}")
|
||||
return len(domains)
|
||||
except Exception as e:
|
||||
logger.error(f"导出CSV文件出错: {e}")
|
||||
raise
|
||||
|
||||
def export_to_txt(self, domains, output_file):
|
||||
"""
|
||||
导出域名到TXT文件,一行一个域名。
|
||||
"""
|
||||
try:
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
for domain in domains:
|
||||
value = domain.get('domain', '').strip()
|
||||
if value:
|
||||
f.write(value + '\n')
|
||||
logger.info(f"成功导出 {len(domains)} 个域名到TXT文件: {output_file}")
|
||||
return len(domains)
|
||||
except Exception as e:
|
||||
logger.error(f"导出TXT文件出错: {e}")
|
||||
raise
|
||||
162
domainCheck/app/core/task_scheduler.py
Normal file
162
domainCheck/app/core/task_scheduler.py
Normal file
@@ -0,0 +1,162 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
'''
|
||||
@Project :domainScanDemo
|
||||
@File :task_scheduler.py
|
||||
@IDE :PyCharm
|
||||
@Author :梦伴
|
||||
@Date :2026/4/8 23:53
|
||||
@explain : 任务调度器
|
||||
'''
|
||||
|
||||
import time
|
||||
import threading
|
||||
from loguru import logger
|
||||
from app.utils.database import Database
|
||||
from app.core.detect_engine import DetectEngine
|
||||
|
||||
|
||||
class TaskScheduler:
|
||||
"""
|
||||
任务调度器
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化任务调度器
|
||||
"""
|
||||
self.db = Database()
|
||||
self.detect_engine = DetectEngine()
|
||||
self.running = False
|
||||
self.threads = []
|
||||
self.max_threads = 10
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
启动任务调度器
|
||||
"""
|
||||
if self.running:
|
||||
logger.info("任务调度器已经在运行中")
|
||||
return
|
||||
|
||||
self.running = True
|
||||
logger.info("启动任务调度器")
|
||||
|
||||
# 启动多个线程处理任务
|
||||
for i in range(self.max_threads):
|
||||
thread = threading.Thread(target=self._process_tasks, daemon=True)
|
||||
thread.start()
|
||||
self.threads.append(thread)
|
||||
logger.info(f"启动任务处理线程 {i+1}")
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
停止任务调度器
|
||||
"""
|
||||
self.running = False
|
||||
logger.info("停止任务调度器")
|
||||
|
||||
# 等待线程结束
|
||||
for thread in self.threads:
|
||||
thread.join(timeout=5)
|
||||
|
||||
self.threads.clear()
|
||||
logger.info("任务调度器已停止")
|
||||
|
||||
def _process_tasks(self):
|
||||
"""
|
||||
处理任务
|
||||
"""
|
||||
while self.running:
|
||||
try:
|
||||
# 获取待执行的任务
|
||||
task = self.db.get_pending_task()
|
||||
if not task:
|
||||
# 没有任务,休眠一段时间
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
task_id = task['id']
|
||||
domain_id = task['domain_id']
|
||||
|
||||
logger.info(f"处理任务: {task_id}, 域名ID: {domain_id}")
|
||||
|
||||
# 执行任务
|
||||
success = self.detect_engine.process_task(task_id)
|
||||
|
||||
if success:
|
||||
logger.info(f"任务处理成功: {task_id}")
|
||||
else:
|
||||
logger.warning(f"任务处理失败: {task_id}")
|
||||
|
||||
# 短暂休眠,避免过于频繁的数据库操作
|
||||
time.sleep(0.1)
|
||||
except Exception as e:
|
||||
logger.error(f"处理任务出错: {e}")
|
||||
# 休眠一段时间,避免出错后无限循环
|
||||
time.sleep(5)
|
||||
|
||||
def add_task(self, domain_id, task_type=1, priority=0):
|
||||
"""
|
||||
添加任务
|
||||
|
||||
:param domain_id: 域名ID
|
||||
:param task_type: 任务类型,1-基础检测,2-深度检测
|
||||
:param priority: 优先级,0-低,1-中,2-高
|
||||
:return: int - 任务ID
|
||||
"""
|
||||
try:
|
||||
task_id = self.db.create_detect_task(domain_id, task_type, priority)
|
||||
logger.info(f"添加任务成功: {task_id}, 域名ID: {domain_id}")
|
||||
return task_id
|
||||
except Exception as e:
|
||||
logger.error(f"添加任务失败: {e}")
|
||||
return None
|
||||
|
||||
def get_task_stats(self):
|
||||
"""
|
||||
获取任务统计信息
|
||||
|
||||
:return: dict - 任务统计信息
|
||||
"""
|
||||
try:
|
||||
return self.db.get_task_statistics()
|
||||
except Exception as e:
|
||||
logger.error(f"获取任务统计信息出错: {e}")
|
||||
return {}
|
||||
|
||||
def retry_failed_tasks(self):
|
||||
"""
|
||||
重试失败的任务
|
||||
|
||||
:return: int - 重试的任务数量
|
||||
"""
|
||||
try:
|
||||
tasks = self.db.get_failed_tasks()
|
||||
retry_count = 0
|
||||
|
||||
for task in tasks:
|
||||
task_id = task['id']
|
||||
self.db.update_task_status(task_id, 0) # 0 表示待执行
|
||||
self.db.update_task_retry_count(task_id, 0) # 重置重试次数
|
||||
retry_count += 1
|
||||
|
||||
logger.info(f"重试 {retry_count} 个失败的任务")
|
||||
return retry_count
|
||||
except Exception as e:
|
||||
logger.error(f"重试失败任务出错: {e}")
|
||||
return 0
|
||||
|
||||
def clear_completed_tasks(self, days=7):
|
||||
"""
|
||||
清理已完成的任务
|
||||
|
||||
:param days: 保留天数
|
||||
:return: int - 清理的任务数量
|
||||
"""
|
||||
try:
|
||||
count = self.db.clear_completed_tasks(days)
|
||||
logger.info(f"清理 {count} 个已完成的任务")
|
||||
return count
|
||||
except Exception as e:
|
||||
logger.error(f"清理已完成任务出错: {e}")
|
||||
return 0
|
||||
Reference in New Issue
Block a user