d
This commit is contained in:
@@ -8,13 +8,78 @@
|
||||
@explain : 域名注册状态检测工具
|
||||
'''
|
||||
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
from datetime import datetime, timezone, timedelta # 日期时间处理,用于时区转换
|
||||
from typing import Optional
|
||||
|
||||
import requests # HTTP请求库
|
||||
from loguru import logger # 日志记录
|
||||
import requests
|
||||
import urllib3
|
||||
|
||||
_DIRECT_HTTP = requests.Session()
|
||||
_DIRECT_HTTP.trust_env = False
|
||||
_DIRECT_HTTP.mount("http://", requests.adapters.HTTPAdapter(pool_connections=256, pool_maxsize=512, max_retries=0))
|
||||
_DIRECT_HTTP.mount("https://", requests.adapters.HTTPAdapter(pool_connections=256, pool_maxsize=512, max_retries=0))
|
||||
_PROXY_MANAGERS = {}
|
||||
_PROXY_MANAGER_LOCK = threading.Lock()
|
||||
|
||||
|
||||
def check_register(domain: str, postfix: str = 'com',proxies: dict = None):
|
||||
def _get_http_manager(proxies: Optional[dict] = None):
|
||||
proxy_url = ""
|
||||
if proxies:
|
||||
proxy_url = str(proxies.get("https") or proxies.get("http") or "").strip()
|
||||
if not proxy_url:
|
||||
return _DIRECT_HTTP
|
||||
with _PROXY_MANAGER_LOCK:
|
||||
manager = _PROXY_MANAGERS.get(proxy_url)
|
||||
if manager is None:
|
||||
manager = requests.Session()
|
||||
manager.trust_env = False
|
||||
manager.mount("http://", requests.adapters.HTTPAdapter(pool_connections=128, pool_maxsize=256, max_retries=0))
|
||||
manager.mount("https://", requests.adapters.HTTPAdapter(pool_connections=128, pool_maxsize=256, max_retries=0))
|
||||
_PROXY_MANAGERS[proxy_url] = manager
|
||||
return manager
|
||||
|
||||
|
||||
def _request_register(url: str, *, proxies: dict = None, timeout=None):
|
||||
manager = _get_http_manager(proxies)
|
||||
request_timeout = timeout
|
||||
if isinstance(timeout, urllib3.Timeout):
|
||||
request_timeout = (timeout.connect_timeout, timeout.read_timeout)
|
||||
return manager.get(url, timeout=request_timeout, proxies=proxies, allow_redirects=True)
|
||||
|
||||
|
||||
def _resolve_register_timeout(proxies: Optional[dict], budget_seconds: Optional[float] = None):
|
||||
if proxies:
|
||||
connect_timeout = float(os.getenv("DOMAINCHECK_REGISTER_PROXY_CONNECT_TIMEOUT", "1.0") or 1.0)
|
||||
read_timeout = float(os.getenv("DOMAINCHECK_REGISTER_PROXY_READ_TIMEOUT", "1.5") or 1.5)
|
||||
total_timeout = float(os.getenv("DOMAINCHECK_REGISTER_PROXY_TOTAL_TIMEOUT", "2.2") or 2.2)
|
||||
else:
|
||||
connect_timeout = float(os.getenv("DOMAINCHECK_REGISTER_DIRECT_CONNECT_TIMEOUT", "1.2") or 1.2)
|
||||
read_timeout = float(os.getenv("DOMAINCHECK_REGISTER_DIRECT_READ_TIMEOUT", "1.8") or 1.8)
|
||||
total_timeout = float(os.getenv("DOMAINCHECK_REGISTER_DIRECT_TOTAL_TIMEOUT", "2.6") or 2.6)
|
||||
connect_timeout = max(0.2, connect_timeout)
|
||||
read_timeout = max(0.3, read_timeout)
|
||||
total_timeout = max(max(connect_timeout, read_timeout), float(total_timeout or 0.0))
|
||||
if budget_seconds not in (None, "", 0, "0"):
|
||||
remaining_budget = max(0.6, float(budget_seconds or 0.0))
|
||||
total_timeout = min(total_timeout, remaining_budget)
|
||||
# requests 只原生支持 (connect, read),这里把剩余预算重新切成更短的
|
||||
# connect/read,避免单次 RDAP 请求把整个步骤预算一次性吃掉。
|
||||
if total_timeout <= 1.0:
|
||||
connect_timeout = min(connect_timeout, 0.35)
|
||||
read_timeout = min(read_timeout, max(0.3, total_timeout - 0.2))
|
||||
else:
|
||||
connect_timeout = min(connect_timeout, max(0.35, total_timeout * 0.35))
|
||||
read_timeout = min(read_timeout, max(0.45, total_timeout - connect_timeout))
|
||||
connect_timeout = max(0.2, min(connect_timeout, total_timeout))
|
||||
read_timeout = max(0.3, min(read_timeout, total_timeout))
|
||||
return urllib3.Timeout(connect=connect_timeout, read=read_timeout, total=total_timeout)
|
||||
|
||||
|
||||
def check_register(domain: str, postfix: str = 'com', proxies: dict = None, budget_seconds: Optional[float] = None):
|
||||
'''
|
||||
检测注册状态
|
||||
:param domain: 待检测域名(不包含后缀)
|
||||
@@ -25,34 +90,31 @@ def check_register(domain: str, postfix: str = 'com',proxies: dict = None):
|
||||
url = f'https://rdap.verisign.com/{postfix}/v1/domain/{domain}' # 构建RDAP查询URL
|
||||
# url = f'https://www.baidu.com' # 构建RDAP查询URL
|
||||
|
||||
# 添加重试机制
|
||||
max_retries = 3
|
||||
for retry in range(max_retries):
|
||||
try:
|
||||
response = requests.get(url, timeout=20, proxies=proxies) # 发送GET请求,设置超时5秒
|
||||
if response.status_code == 200: # HTTP 200表示域名已注册
|
||||
json_data = response.json() # 解析JSON响应
|
||||
for item in json_data['events']: # 遍历事件列表
|
||||
if item['eventAction'] == 'expiration': # 如果是过期时间事件
|
||||
utc_time = datetime.fromisoformat(item['eventDate'].replace('Z', '+00:00')) # 解析UTC时间
|
||||
beijing_tz = timezone(timedelta(hours=8)) # 创建北京时区(UTC+8)
|
||||
beijing_time = utc_time.astimezone(beijing_tz) # 将UTC时间转换为北京时间
|
||||
return 3, beijing_time.strftime("%Y-%m-%d %H:%M:%S") # 返回已注册状态和过期时间
|
||||
|
||||
elif response.status_code == 404: # HTTP 404表示域名不存在,可注册
|
||||
# 可注册状态
|
||||
return 2, '' # 返回可注册状态,过期时间为空
|
||||
logger.error(response.status_code)
|
||||
except requests.exceptions.RequestException as e:
|
||||
# logger.warning(f"注册状态检测第{retry+1}次失败: {domain}, 错误: {e}")
|
||||
if retry < max_retries - 1:
|
||||
import time
|
||||
time.sleep(2) # 等待2秒后重试
|
||||
proxies=None
|
||||
else:
|
||||
# 达到最大重试次数,返回检测失败
|
||||
logger.error(f"注册状态检测失败: {domain}, 已达到最大重试次数")
|
||||
return -1, '' # -1表示检测失败,过期时间为空
|
||||
mode = "proxy" if proxies else "direct"
|
||||
timeout = _resolve_register_timeout(proxies, budget_seconds=budget_seconds)
|
||||
response = None
|
||||
try:
|
||||
response = _request_register(url, proxies=proxies, timeout=timeout)
|
||||
except Exception as e:
|
||||
logger.warning(f"注册状态检测请求异常: {domain}, 模式: {mode}, 错误: {e}")
|
||||
raise
|
||||
|
||||
if int(response.status_code) == 200: # HTTP 200表示域名已注册
|
||||
json_data = response.json() if response.content else {} # 解析JSON响应
|
||||
for item in json_data['events']: # 遍历事件列表
|
||||
if item['eventAction'] == 'expiration': # 如果是过期时间事件
|
||||
utc_time = datetime.fromisoformat(item['eventDate'].replace('Z', '+00:00')) # 解析UTC时间
|
||||
beijing_tz = timezone(timedelta(hours=8)) # 创建北京时区(UTC+8)
|
||||
beijing_time = utc_time.astimezone(beijing_tz) # 将UTC时间转换为北京时间
|
||||
return 3, beijing_time.strftime("%Y-%m-%d %H:%M:%S") # 返回已注册状态和过期时间
|
||||
return 3, ''
|
||||
|
||||
if int(response.status_code) == 404: # HTTP 404表示域名不存在,可注册
|
||||
return 2, '' # 返回可注册状态,过期时间为空
|
||||
|
||||
error_message = f"rdap unexpected status {response.status_code}"
|
||||
logger.warning(f"注册状态检测返回异常状态码: {domain}, 模式: {mode}, 状态码: {response.status_code}")
|
||||
raise RuntimeError(error_message)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user