過期網域機會搜尋器
針對特定利基市場評估過期網域候選名單。根據主題契合度、歷史活躍程度、紀錄乾淨度及重導向適合性進行評分。輸出一個保守且可解釋的精選清單,供人工審核。
關鍵規則: 每項推薦都必須同時包含正面理由(why_selected)與謹慎理由(why_risky)。絕不能僅輸出分數而不加說明。
保守預設規則: 當信號不完整或相互矛盾時,應降低信心水準。不要將模棱兩可的候選網域視為強烈機會。資料缺失會降低信心,絕不應人為提高。
反濫用規則: 絕不鼓勵不相關的重導向、PBN 建置或將網域用於與其歷史主題不符的其他用途。請閱讀 references/guardrails.md 以了解完整的反濫用政策。
步驟 1:環境檢查
在執行任何其他操作之前,先檢查環境。
驗證 curl 和 python3(或 python)是否可用:
curl --version > /dev/null 2>&1 && echo "curl: 可用" || echo "curl: 缺失"
python3 --version 2>/dev/null || python --version 2>/dev/null || echo "python: 缺失"
檢查可選的 LLM API 金鑰,以進行增強的主題契合度評分:
echo "LLM_API_KEY: ${LLM_API_KEY:+已設定}"
如果 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(字串):要評估的核心利基市場。範例:"developer tools"、"AI SaaS"、"cybersecurity"、"fintech"。
可選(僅在未提供時詢問):
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' 來使用針對「developer tools」利基的內建示範資料」
如果使用者輸入 'example': 使用此示範資料:
devtoolsweekly.com
codeshipnews.io
stackforgeapp.com
quickseorank.net
bestcheaphosting247.com
cloudbuildpro.dev
reactwidgetlib.com
megadealsshop.xyz
收集所有輸入後,進行確認: 「目標利基:[利基]。正在評估 [N] 個候選網域。評分模式:[模式]。預期用途:[用途]。」
步驟 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 '找不到標題'
# 同時提取 meta 描述
meta_match = re.search(r'<meta[^>]*name=[\"']description[\"'][^>]*content=[\"'](.*?)[\"']', html, re.IGNORECASE)
desc = meta_match.group(1).strip() if meta_match else '找不到描述'
print(f'標題:{title}')
print(f'描述:{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 = '未知'
created = '未知'
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', '未知')
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] 個候選網域完成信號收集。[M] 個 Wayback 命中,[K] 個 RDAP 查詢成功。」
步驟 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% | |
| 與目標利基比對 | low_topic_overlap |
| 歷史快照中偵測到大量 4xx/5xx 狀態碼 | history_has_errors |
| 歷史標題或描述中存在垃圾郵件指標(高密度完全比對、過多「免費」或「優惠」等用詞) | spammy_history |
| 網域已註冊或 RDAP 顯示近期創建日期 | currently_registered |
| 中性或負面的 LLM 重導向可行性分數 (< 5) | redirect_not_recommended |
| 所有信號源的平均資料完整度低(< 2 個面向有強力資料) | low_signal_integrity |
快照數量低於 min_snapshots 門檻或活躍年數 < 2 | limited_history |
風險評級與過濾: 根據套用的標記決定網域的風險評級:
- 如果出現
spammy_history或currently_registered→ 風險評級 =high - 如果
max_risk_level為low或medium,這些網域預設會被排除 - 如果出現
low_topic_overlap或low_signal_integrity→ 風險評級 =medium - 否則 → 風險評級 =
low
注意: 即使風險評級為 high,如果使用者要求顯示,某些標記仍應以附註方式顯示,而不是完全隱藏網域。請使用 risk_flags 陣列呈現所有標記。
步驟 7:排名與輸出
為每個候選網域建構一個輸出物件,按 opportunity_score 降序排序。
對於每個網域,輸出:
domain(字串)opportunity_score(0–100 整數)confidence(low、medium、high)recommended_action(rebuild、redirect、skip)why_selected(字串):一至兩句說明為何此網域可能是良好的機會。參考關鍵評分面向與亮點。why_risky(字串):一至兩句說明此網域的疑慮與風險。參考套用的風險標記。dimension_scores(物件):topical_relevance(整數 0–30)historical_activity(整數 0–25)content_quality(整數 0–15)history_cleanliness(整數 0–15)redirect_suitability(整數 0–10)signal_completeness(整數 0–5)
risk_flags(字串陣列):所有對該網域套用的風險標記。
輸出格式: 以一個清楚的 Markdown 表格呈現排名清單,包含以下欄位:排名、網域、分數、信心、建議動作、選擇理由、風險理由。在表格下方,列出所有套用的標記。
範例輸出摘要區塊:
## 機會排名
| 排名 | 網域 | 分數 | 信心 | 建議動作 | 選擇理由 | 風險理由 |
|---|---|---|---|---|---|---|
| 1 | devtoolsweekly.com | 85 | 高 | 重建 | 主題契合度極佳:高關鍵字比對、歷史內容為開發工具。Wayback 記錄豐富(>500 個快照,8 年活躍)。 | RDAP 顯示目前為註冊狀態。需要監控釋出。|
| 2 | codeshipnews.io | 72 | 中 | 重建 | 良好的主題契合度與開發工具利基重疊。4 年活躍歷史。 | 歷史內容有些許商業廣告語氣(高密度「優惠」)。快照數量中等。|
**風險摘要:**
- devtoolsweekly.com:`currently_registered`
- codeshipnews.io:`spammy_history`、`limited_history`
步驟 8:摘要與下一步
在最終輸出之後,提供:
- 評估的候選總數
- 高信心機會的數量(分數 > 70 且信心為高)
- 中信心機會的數量(分數 > 50)
- 標記為高風險的數量
- 請使用者審查排名清單並選擇網域進行進一步盡職調查(例如,檢閱完整 Wayback 記錄、執行病毒掃描、檢查反向連結設定檔)
參考資料
此技能依賴於以下參考檔案,這些檔案應存在於該技能的 references/ 目錄中:
references/scoring-model.md— 完整的評分方法說明references/risk-flags.md— 完整的風險標記定義與嚴重性references/guardrails.md— 反濫用政策與限制
如果任何參考檔案缺失,請說明:「缺少評分或風險參考資料。將使用預設保守值繼續執行。」