过期域名机会查找器
评估特定利基市场的过期域名候选对象。根据主题契合度、历史活动水平、历史清洁度和重定向适用性进行评分。输出一份保守且可解释的短名单供人工审核。
关键规则: 每个推荐必须同时包含正面理由 (why_selected) 和谨慎理由 (why_risky)。切勿输出不附带解释的裸分数。
保守默认规则: 当信号不完整或矛盾时,降低置信度。不要将有歧义的候选对象视为强机会。数据缺失会降低置信度,绝不应提高。
反滥用规则: 切勿鼓励不相关的重定向、PBN(私人博客网络,Private Blog Network)构建或历史主题与目标利基市场不匹配的域名改作他用。请阅读 references/guardrails.md 了解完整的反滥用政策。
第 1 步:环境检查
在执行任何操作之前检查环境。
验证 curl 和 python3(或 python)是否可用:
curl --version > /dev/null 2>&1 && echo "curl: available" || echo "curl: MISSING"
python3 --version 2>/dev/null || python --version 2>/dev/null || echo "python: MISSING"
检查是否存在可选的 LLM API 密钥,用于增强利基市场相关性评分:
echo "LLM_API_KEY: ${LLM_API_KEY:+set}"
如果缺少 curl 或 python:
停止。告知用户:“此技能需要 curl 和 Python 3.10+。请安装它们后重试。”
如果未设置 LLM_API_KEY:
继续。技能将仅使用基于规则的评分(域名字符串匹配、Wayback 标题分析、关键词重叠)。通知用户:“正在以纯规则模式运行。设置 LLM_API_KEY 以启用增强型利基市场相关性评分。”
如果已设置 LLM_API_KEY:
技能将使用 LLM 增强的评分进行主题相关性分析。这提供了对利基市场契合度的更深层上下文评估。
QA:声明评分模式(llm-enhanced 或 rule-based-only)并确认工具可用。
第 2 步:输入收集
从用户处收集必需和可选的输入。
必需:
target_niche(字符串):要评估的核心利基市场。示例:“开发者工具”、“AI SaaS”、“网络安全”、“金融科技”。
可选(仅当未提供时询问):
seed_keywords(数组):用于细化主题匹配的关键词。如果未提供,则自动从利基市场名称中提取 3-5 个关键词。candidate_domains(数组):要评估的特定域名。如果未提供,提示用户。discovery_source(字符串):候选对象的来源——manual、expireddomains-net、external-feed。min_snapshots(整数):最小历史快照阈值。默认值:10。max_risk_level(字符串):low、medium或high。控制筛选有风险候选对象的激进程度。默认值:medium。intended_use(字符串):rebuild、redirect或either。默认值:either。
如果未提供 candidate_domains:
询问:“请提供要评估的过期域名候选列表。您可以:
- 粘贴域名(每行一个或用逗号分隔)
- 提供文本文件的路径,每行一个域名
- 说 'example' 以针对“开发者工具”利基市场使用内置演示集运行”
如果用户说 'example': 使用此演示集:
devtoolsweekly.com
codeshipnews.io
stackforgeapp.com
quickseorank.net
bestcheaphosting247.com
cloudbuildpro.dev
reactwidgetlib.com
megadealsshop.xyz
收集完所有输入后,确认: “目标利基市场:[niche]。正在评估 [N] 个候选域名。评分模式:[mode]。预期用途:[use]。”
第 3 步:候选对象标准化
在评分之前清理并验证候选列表。
python3 -c "
import sys, re
domains = '''CANDIDATE_LIST_HERE'''.strip().split('\n')
seen = set()
valid = []
invalid = []
for d in domains:
d = d.strip().lower()
# 去除协议和路径
d = re.sub(r'^https?://', '', d)
d = d.split('/')[0]
d = d.strip('.')
if not d:
continue
# 基本 TLD 验证
if '.' not in d or len(d) < 4:
invalid.append(d)
continue
# 去重
if d in seen:
continue
seen.add(d)
valid.append(d)
print(f'有效候选对象:{len(valid)}')
print(f'已移除(无效/重复):{len(invalid)}')
for v in valid:
print(f' ✓ {v}')
for i in invalid:
print(f' ✗ {i}(格式无效)')
"
将 CANDIDATE_LIST_HERE 替换为第 2 步中的实际域名列表。
声明:“标准化后 [N] 个有效候选对象。已移除 [M] 个(无效/重复)。”
如果有效候选对象为 0,则停止并告知用户:“未找到有效的域名候选对象。请提供格式为 'example.com' 的域名。”
第 4 步:信号收集
对于每个有效候选对象,从免费的公共来源收集信号。 按顺序对每个域名运行这些检查。
4a:Wayback CDX API — 历史快照
查询 Wayback Machine 以获取所有历史快照。我们使用 limit=100000,并有目的地省略了显式的 from/to 参数,以便 CDX 返回域名整个生命周期内的快照。结果按时间戳升序排序(最早的在前),因此 first_capture 和 last_capture 是准确的:
curl -s "https://web.archive.org/cdx/search/cdx?url=DOMAIN_HERE&output=json&fl=timestamp,statuscode&collapse=timestamp:6&limit=100000" \
| python3 -c "
import sys, json
try:
data = json.load(sys.stdin)
if len(data) <= 1:
print(json.dumps({'domain': 'DOMAIN_HERE', 'snapshots': 0, 'first_capture': None, 'last_capture': None, 'status_codes': {}, 'years_active': 0}))
else:
rows = data[1:] # 跳过标题行
timestamps = [r[0] for r in rows] # 已经升序排列(最早的在前)
statuses = [r[1] for r in rows]
status_counts = {}
for s in statuses:
status_counts[s] = status_counts.get(s, 0) + 1
first_year = int(timestamps[0][:4])
last_year = int(timestamps[-1][:4])
print(json.dumps({
'domain': 'DOMAIN_HERE',
'snapshots': len(rows),
'first_capture': timestamps[0],
'last_capture': timestamps[-1],
'status_codes': status_counts,
'years_active': last_year - first_year + 1
}))
except:
print(json.dumps({'domain': 'DOMAIN_HERE', 'snapshots': 0, 'error': 'wayback_api_failed'}))
"
速率限制: 每次 Wayback API 调用之间等待 2 秒,以示礼貌。
4b:Wayback 内容采样 — 历史页面标题
对于快照数 > 0 的候选对象,获取最新的快照以提取页面标题(用于主题相关性评分):
curl -s -L "https://web.archive.org/web/LATEST_TIMESTAMP/http://DOMAIN_HERE" \
| python3 -c "
import sys, re
html = sys.stdin.read()[:50000]
title_match = re.search(r'<title[^>]*>(.*?)</title>', html, re.IGNORECASE | re.DOTALL)
title = title_match.group(1).strip() if title_match else 'no title found'
# 也提取 meta 描述
meta_match = re.search(r'<meta[^>]*name=[\"']description[\"'][^>]*content=[\"'](.*?)[\"']', html, re.IGNORECASE)
desc = meta_match.group(1).strip() if meta_match else 'no description found'
print(f'Title: {title}')
print(f'Description: {desc}')
"
将 LATEST_TIMESTAMP 替换为第 4a 步中的最新时间戳。
4c:RDAP 查询 — 注册状态
使用跨平台的基于 HTTP 的 RDAP 标准(取代依赖于操作系统的 WHOIS)。来自 RDAP 的 HTTP 404 意味着域名未注册(即它确实是可用的或未被跟踪)——这与网络故障不同。明确处理这两种情况:
python3 -c "
import urllib.request, urllib.error, json
domain = 'DOMAIN_HERE'
try:
req = urllib.request.Request(
f'https://rdap.org/domain/{domain}',
headers={'User-Agent': 'Mozilla/5.0'}
)
with urllib.request.urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode())
registrar = 'unknown'
created = 'unknown'
for entity in data.get('entities', []):
if 'registrar' in entity.get('roles', []):
try:
registrar = entity.get('vcardArray', [[]])[1][0][3]
except Exception:
pass
for event in data.get('events', []):
if event.get('eventAction') == 'registration':
created = event.get('eventDate', 'unknown')
print(json.dumps({
'domain': domain,
'status': 'registered',
'registrar': registrar,
'created': created
}))
except urllib.error.HTTPError as e:
if e.code == 404:
# 域名没有 RDAP 对象——可能未注册或不在 RDAP 覆盖范围内
print(json.dumps({'domain': domain, 'status': 'unregistered_or_no_rdap_object'}))
else:
print(json.dumps({'domain': domain, 'error': f'rdap_http_error_{e.code}'}))
except Exception:
print(json.dumps({'domain': domain, 'error': 'rdap_lookup_failed'}))
"
4d:域名字符串分析 — 关键词匹配
对域名与目标利基市场/种子关键词之间的关键词重叠进行评分:
python3 -c "
import re, json
domain = 'DOMAIN_HERE'
niche = 'NICHE_HERE'
seeds = SEEDS_JSON_HERE # 例如 ['devops', 'ci/cd', 'code editor']
# 从域名中提取单词
domain_base = domain.rsplit('.', 1)[0] # 去除 TLD
domain_words = re.split(r'[-_.]', domain_base.lower())
# 检查利基市场单词
niche_words = niche.lower().split()
all_keywords = set(niche_words + [s.lower() for s in seeds])
matches = [w for w in domain_words if any(kw in w or w in kw for kw in all_keywords)]
match_ratio = len(matches) / max(len(domain_words), 1)
print(json.dumps({
'domain': domain,
'domain_words': domain_words,
'keyword_matches': matches,
'match_ratio': round(match_ratio, 2)
}))
"
4e:Gemini LLM 利基市场相关性评估(如果设置了 LLM_API_KEY)
如果配置了 LLM API 密钥,则批量处理所有候选对象及其收集到的信号,并要求进行上下文的利基市场相关性评估。
注意: 下面的请求/响应格式使用了 Gemini API(generateContent 格式)。未经修改,它与 OpenAI 风格的端点不兼容。如果您使用不同的提供商,则必须调整 JSON 主体和响应解析。
cat > /tmp/domain-relevance-request.json << 'ENDJSON'
{
"system_instruction": {
"parts": [{
"text": "您是一名 SEO 研究分析师。对于提供的每个过期域名候选对象,请评估其与指定目标利基市场的主题相关性。考虑域名、历史页面标题和 meta 描述。对于每个域名,输出一个 JSON 对象,包含:domain(字符串)、relevance_score(整数 1-10)、relevance_rationale(一句话解释分数)、redirect_plausibility(整数 1-10)、redirect_rationale(一句话)。仅输出一个 JSON 数组。前后不要带任何评论。"
}]
},
"contents": [{
"parts": [{
"text": "DOMAIN_SIGNALS_AND_NICHE_CONTEXT_HERE"
}]
}],
"generationConfig": {
"temperature": 0.2,
"maxOutputTokens": 2048
}
}
ENDJSON
将 DOMAIN_SIGNALS_AND_NICHE_CONTEXT_HERE 替换为:
- 目标利基市场和种子关键词
- 对于每个候选对象:域名、历史标题、描述、关键词匹配数据
向 Gemini API 发送请求:
curl -s -X POST \
"${LLM_API_ENDPOINT:-https://generativelanguage.googleapis.com/v1beta}/models/${LLM_MODEL:-gemini-2.0-flash}:generateContent?key=$LLM_API_KEY" \
-H "Content-Type: application/json" \
-d @/tmp/domain-relevance-request.json \
| python3 -c "
import sys, json
try:
d = json.load(sys.stdin)
text = d['candidates'][0]['content']['parts'][0]['text']
print(text)
except (KeyError, IndexError, json.JSONDecodeError) as e:
print(json.dumps({'error': 'llm_response_parse_failed', 'detail': str(e)}))
"
如果 LLM 调用或响应解析失败,记录错误并仅继续使用基于规则的评分。不要停止工作流。
所有信号收集完成后,声明: “[N] 个候选对象的信号收集完成。Wayback 命中 [M] 次,RDAP 查询成功 [K] 次。”
第 5 步:评分与分类
阅读 references/scoring-model.md 了解完整的评分框架。
对于每个候选对象,在 6 个维度上计算分数:
-
主题相关性 (0–30): 结合域名关键词匹配率、历史标题/描述分析以及 LLM 相关性分数(如果可用)。无 LLM:使用关键词匹配率 × 15 + 标题关键词重叠 × 15。有 LLM:使用 LLM relevance_score × 3。
-
历史活动水平 (0–25): 基于 Wayback 快照多样性和频率。跨越多年一致捕获的更多快照表明更高的持续活动和推断的合法性。
-
历史内容质量 (0–15): 源于历史页面标题和 meta 描述分析,检查自然措辞与关键词堆砌。无 LLM:基本分数 8/15,根据精确匹配密度进行调整。
-
历史清洁度 (0–15): 基于 Wayback 快照数量、活跃年限、状态码一致性以及是否存在停放页面指示器。
-
重定向适用性 (0–10): 基于历史内容与目标利基市场之间的主题连续性。如果可用则使用 LLM redirect_plausibility 分数;否则使用关键词重叠率。
-
信号完整性 (0–5): 统计返回可用数据的数据源数量(Wayback、RDAP、域名分析、LLM(如果已配置))。
计算:
opportunity_score= 所有维度分数之和(0–100)confidence= 基于拥有强数据的维度数量(请参阅 scoring-model.md)recommended_action= 基于分数 + 置信度 + 风险标记(请参阅第 6 步)
第 6 步:风险标记与过滤
阅读 references/risk-flags.md 了解完整的标记定义。
将风险标记应用于每个候选对象:
| 检查项 | 应用标记 |
|---|---|
| 历史主题重叠 < 30% 的目标利基市场 | topic_mismatch |
| 历史快照中的域名显示已知的垃圾邮件模式(例如,重定向到垃圾网站) | spam_history |
| 域名被 Google Safe Browsing 或类似服务标记为恶意软件 | malware_flag |
| RDAP 状态显示域名“已注册”,但没有任何历史快照——可能刚刚注册且未使用 | suspicious_new_registration |
| 域名字符串包含明显的关键词堆砌模式(例如,“bestcheaphosting247.com”) | keyword_stuffing |
| 历史内容质量分数非常低(< 4/15) | low_content_quality |
保守过滤: 如果 max_risk_level 是 low,则移除所有带有 topic_mismatch 或 malware_flag 标记的候选对象。如果 medium(默认),则仅移除带有 malware_flag 的候选对象。如果 high,则不移除任何候选对象,但保留所有标记以供人工审核。
第 7 步:输出排名短名单
生成一份按 opportunity_score 降序排序的 JSON 短名单。每个条目 必须 包含 why_selected(正面理由)和 why_risky(谨慎理由),就像在“关键规则”中要求的那样。
{
"target_niche": "[niche]",
"scoring_mode": "[mode]",
"intended_use": "[use]",
"total_candidates_evaluated": [N],
"candidates_ranked": [
{
"rank": 1,
"domain": "devtoolsweekly.com",
"overall_score": 78,
"dimension_scores": {
"topical_relevance": 24,
"historical_activity": 20,
"content_quality": 12,
"history_cleanliness": 10,
"redirect_suitability": 8,
"signal_completeness": 4
},
"confidence": "high",
"recommended_action": "rebuild",
"why_selected": "该域名包含高度相关的关键词 'devtools' 和 'weekly',与目标利基市场(开发者工具)非常匹配。历史 Wayback 快照显示了一个具有高质量内容、定期更新的时尚科技博客。RDAP 查找显示该域名似乎未注册,为重建提供了可用的机会。",
"why_risky": "该域名在 2020-2022 年之间有一个垃圾链接迁移期,在此期间它短暂地重定向到一个通用自建链接网络(PBN)群集。虽然此活动在两年前停止了,但一些链接配置文件可能会保持暂时的风险。此外,从 Wayback 捕获的后期标题显示出一些关键词堆砌的迹象,将置信度从更高水平略微降低。",
"risk_flags": ["spam_history"]
}
]
}
必须包含一个有序的排名列表。每个条目都必须有 rank、domain、overall_score、dimension_scores、confidence、recommended_action、why_selected、why_risky 和 risk_flags。
第 8 步:用户解释
向用户简要总结结果:
“为 [niche] 找到 [N] 个候选对象。
最强的机会是:[domain1]([score1] 分),[domain2]([score2] 分)。
所有推荐详情都在上面的 JSON 短名单中。
下一步: 查看顶级候选对象的来源分析、配置您的工具,然后手动验证机会。
警告: 仅当目标利基市场与网站历史内容对齐时,才批准重定向或重建。参见 references/guardrails.md 获取完整指南。”
版本:1.0 | 更新:[当前日期是 [2025 年 3 月]]