WHOIS 门户查询到第 3~5 次通常就会弹出验证码——这是 ICANN 官方查询页、注册商查询页面乃至 APNIC、RIPE 等区域级互联网注册管理机构的通用防护策略,不是针对某个 IP 的临时封锁。批量核实域名可用性、验证所有权、定期检查到期时间,只要请求量上去了,reCAPTCHA v2、图片验证码或 Cloudflare Turnstile 迟早会挡在中间。把 CaptchaAI 的识别能力接入查询脚本,遇到验证码就自动识别继续跑。
验证码什么时候会挡住 WHOIS 查询
- ICANN WHOIS:reCAPTCHA v2,每个会话 3–5 次查询后触发
- 注册商自建查询页:reCAPTCHA v2/v3,每分钟 5–10 次查询后触发
- 区域级 NIR(APNIC、RIPE):图片验证码,10–20 次查询后触发
- 域名拍卖平台 WHOIS:Cloudflare Turnstile,连续查域时触发
- 批量 WHOIS 工具:自定义验证码,超出免费额度后触发
先把稳定性和报错处理讲清楚
提升查询稳定性的几个技巧
| 优化技巧 | 效果 |
|---|---|
| 本地缓存查询结果 | 避免重复查询同一域名 |
| 请求间隔设置为 3–5 秒 | 降低验证码触发概率 |
| 在多个 WHOIS 门户之间轮换 | 分散请求负载 |
| 保持会话状态 | 减少重复验证码挑战 |
常见报错排查
| 问题 | 原因 | 处理方式 |
|---|---|---|
| 查询 3 次左右就弹验证码 | 门户按 IP/会话限流 | 增加间隔,或用自有服务器基础设施分摊 |
| WHOIS 返回“无匹配” | 隐私/RDAP 数据编辑 | 换一个 WHOIS 门户重试 |
| reCAPTCHA token 被拒绝 | 提交前 token 已过期 | 识别完成后 2 分钟内提交 |
| IP 被封 | 单 IP 每日查询超限 | 轮换自有服务器基础设施出口 |
批量采集和存储 WHOIS 数据时,留意查询对象所在司法辖区的数据合规要求,比如国内的《数据安全法》与《个人信息保护法》——只处理你有权处理的数据。
Python 批量查询:识别 reCAPTCHA v2 后继续跑
给新项目选域名时,你可能要一次性核对几十个 .com/.io/.dev 候选词的注册状态,手动开 WHOIS 页面太慢。下面这段代码检测到 reCAPTCHA 后自动提取 sitekey,调用 CaptchaAI 识别,把 token 提交回表单继续拿数据。
小贴士:
pip install慢的话加个国内镜像,比如pip install -i https://pypi.tuna.tsinghua.edu.cn/simple beautifulsoup4 requests。
import requests
import time
import re
class WhoisLookup:
def __init__(self, api_key):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def lookup(self, domain, whois_url):
"""Look up WHOIS data for a domain, solving CAPTCHAs as needed."""
response = self.session.get(whois_url, params={"domain": domain})
if self._has_recaptcha(response.text):
site_key = self._extract_site_key(response.text)
token = self._solve_recaptcha(site_key, whois_url)
response = self.session.post(whois_url, data={
"domain": domain,
"g-recaptcha-response": token
})
return self._parse_whois(response.text)
def bulk_lookup(self, domains, whois_url, delay=3):
"""Look up WHOIS for multiple domains."""
results = {}
for domain in domains:
try:
results[domain] = self.lookup(domain, whois_url)
except Exception as e:
results[domain] = {"error": str(e)}
time.sleep(delay)
return results
def check_availability(self, domains, whois_url):
"""Check which domains are available for registration."""
results = self.bulk_lookup(domains, whois_url)
available = []
taken = []
for domain, data in results.items():
if data.get("error") or data.get("status") == "available":
available.append(domain)
else:
taken.append(domain)
return {"available": available, "taken": taken}
def _has_recaptcha(self, html):
return "g-recaptcha" in html or "recaptcha" in html.lower()
def _extract_site_key(self, html):
match = re.search(r'data-sitekey="([^"]+)"', html)
if match:
return match.group(1)
raise ValueError("reCAPTCHA site key not found")
def _solve_recaptcha(self, site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": self.api_key,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"]
raise TimeoutError("reCAPTCHA solve timed out")
def _parse_whois(self, html):
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
# Look for WHOIS data in pre-formatted blocks or tables
raw_whois = soup.select_one("pre, .whois-data, #whois-result")
if raw_whois:
text = raw_whois.get_text()
return self._extract_fields(text)
return {"raw": soup.get_text()[:2000]}
def _extract_fields(self, text):
fields = {}
patterns = {
"registrar": r"Registrar:\s*(.+)",
"created": r"Creat(?:ed|ion) Date:\s*(.+)",
"expires": r"(?:Expir(?:y|ation)|Registry Expiry) Date:\s*(.+)",
"updated": r"Updated Date:\s*(.+)",
"status": r"(?:Domain )?Status:\s*(.+)",
"nameservers": r"Name Server:\s*(.+)",
"registrant": r"Registrant (?:Name|Organization):\s*(.+)"
}
for field, pattern in patterns.items():
matches = re.findall(pattern, text, re.IGNORECASE)
if matches:
fields[field] = matches if len(matches) > 1 else matches[0].strip()
return fields
# Usage
whois = WhoisLookup("YOUR_API_KEY")
# Single lookup
result = whois.lookup("example.com", "https://whois.example.com/lookup")
print(f"Registrar: {result.get('registrar')}")
print(f"Expires: {result.get('expires')}")
# Bulk availability check
domains = ["startup-name.com", "my-project.io", "cool-app.dev"]
availability = whois.check_availability(domains, "https://whois.example.com/lookup")
print(f"Available: {availability['available']}")
JavaScript 域名到期监控:定时任务 + 验证码识别
手上有一批域名资产时,到期监控比一次性可用性查询更实用——续费窗口错过,域名可能被别人抢注。下面这段 Node.js 代码维护监控列表,定期查询,遇到 reCAPTCHA 自动识别后继续,到期日剩 30 天以内输出提醒。
class DomainMonitor {
constructor(apiKey) {
this.apiKey = apiKey;
this.watchList = new Map();
}
addDomain(domain, whoisUrl) {
this.watchList.set(domain, { url: whoisUrl, history: [] });
}
async checkExpirations() {
const expiring = [];
for (const [domain, config] of this.watchList) {
try {
const data = await this.lookup(domain, config.url);
config.history.push({ ...data, checkedAt: new Date().toISOString() });
if (data.expires) {
const daysLeft = Math.ceil(
(new Date(data.expires) - new Date()) / (1000 * 60 * 60 * 24)
);
if (daysLeft <= 30) {
expiring.push({ domain, daysLeft, expires: data.expires });
}
}
} catch (error) {
console.error(`Failed to check ${domain}: ${error.message}`);
}
}
return expiring;
}
async lookup(domain, whoisUrl) {
const response = await fetch(`${whoisUrl}?domain=${domain}`);
const html = await response.text();
if (html.includes('g-recaptcha')) {
return this.solveAndLookup(domain, whoisUrl, html);
}
return this.parseWhois(html);
}
async solveAndLookup(domain, whoisUrl, html) {
const match = html.match(/data-sitekey="([^"]+)"/);
if (!match) throw new Error('No reCAPTCHA site key found');
const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
method: 'POST',
body: new URLSearchParams({
key: this.apiKey,
method: 'userrecaptcha',
googlekey: match[1],
pageurl: whoisUrl,
json: '1'
})
});
const { request: taskId } = await submitResp.json();
for (let i = 0; i < 60; i++) {
await new Promise(r => setTimeout(r, 3000));
const result = await fetch(
`https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
);
const data = await result.json();
if (data.status === 1) {
const response = await fetch(whoisUrl, {
method: 'POST',
body: new URLSearchParams({
domain,
'g-recaptcha-response': data.request
})
});
return this.parseWhois(await response.text());
}
}
throw new Error('reCAPTCHA solve timed out');
}
parseWhois(html) {
const extract = (pattern) => {
const match = html.match(pattern);
return match ? match[1].trim() : null;
};
return {
registrar: extract(/Registrar:\s*([^\n<]+)/i),
created: extract(/Creat(?:ed|ion) Date:\s*([^\n<]+)/i),
expires: extract(/(?:Expir(?:y|ation)|Registry Expiry) Date:\s*([^\n<]+)/i),
status: extract(/(?:Domain )?Status:\s*([^\n<]+)/i)
};
}
}
// Usage
const monitor = new DomainMonitor('YOUR_API_KEY');
monitor.addDomain('example.com', 'https://whois.example.com/lookup');
monitor.addDomain('mysite.io', 'https://whois.example.com/lookup');
const expiring = await monitor.checkExpirations();
expiring.forEach(d => console.log(`${d.domain} expires in ${d.daysLeft} days`));
常见问题
国内网络访问 reCAPTCHA 脚本较慢,会影响批量查询吗?
会。reCAPTCHA v2 加载 Google 托管的脚本,部分国内网络环境下访问不稳定,可能拖慢识别流程。建议设置足够的超时与重试,选连通性稳定的出口环境跑脚本。
批量查询时怎样避免被 WHOIS 门户封锁 IP?
查询间隔控制在 3–5 秒以上,结合本地缓存避免重复查询。查询量大时用自有服务器基础设施分散出口,并在多个门户间轮换。
WHOIS 门户除了 reCAPTCHA v2,还会遇到哪些验证码?
常见的还有图片验证码(多见于 APNIC、RIPE)和 Cloudflare Turnstile(常见于域名拍卖平台)。CaptchaAI 对这几类都有对应的识别方法。
端口 43 的 WHOIS 协议要验证码吗?
不需要,但受 RDAP 隐私政策限制,返回字段比网页门户少,查询频率控制也更严。网页门户虽然会挡验证码,通过后往往能看到更完整的字段。
能不能自动监控域名到期日期?
可以。给关注的域名建监控列表,按天或按周查询,遇到验证码让 CaptchaAI 自动识别,到期日低于设定阈值就触发提醒。
相关文章
- 用 API 识别 reCAPTCHA v2 回调型验证码
- reCAPTCHA v2 与 Turnstile 同站点处理方案
- reCAPTCHA v2 回调机制详解
下一步
想把域名批量查询、可用性检查和到期监控串成一条不会被验证码打断的流水线?注册 CaptchaAI 拿到 API Key,把上面的示例接进你自己的查询脚本。