عند أتمتة استعلامات WHOIS نادراً ما تتجاوز ثلاثة إلى خمسة استعلامات قبل أن تعترضك reCAPTCHA v2 وتتوقف عملية الجمع. الحل بسيط من حيث المبدأ ويقوم على أربع خطوات: اكتشف وجود الكابتشا في الصفحة، أرسل مفتاح الموقع إلى CaptchaAI، استطلع النتيجة حتى يصل الرمز المحلول، ثم أعد إرسال الطلب مع الرمز لتتابع قراءة بيانات النطاق. هذا الدليل يحوّل هذه الخطوات إلى مسار تشغيلي قابل للتكرار للتحقق من توفّر الأسماء ومراقبة تواريخ التجديد ومطابقة بيانات المُسجِّل.
لماذا تظهر اختبارات CAPTCHA على بوابات WHOIS؟
بيانات تسجيل النطاق عامة، لكن المُسجّلين والسجلات الإقليمية يحمون واجهات البحث من الاستخدام المكثّف عبر تحديد معدل الطلبات وطبقات كابتشا متدرجة. أول استعلام أو اثنين يمرّان بسلاسة، ثم يبدأ ظهور reCAPTCHA v2 أو كابتشا الصور لكبح الطلبات الآلية. معرفة نوع الكابتشا المتوقّع على كل بوابة يساعدك على ضبط الإيقاع مسبقاً بدل مواجهة الحظر لاحقاً.
| نوع البوابة | نوع الكابتشا | عتبة التشغيل |
|---|---|---|
| هيئة ICANN WHOIS | reCAPTCHA v2 | 3–5 استعلامات لكل جلسة |
| صفحات بحث المُسجِّلين | reCAPTCHA v2/v3 | 5–10 استعلامات في الدقيقة |
| السجلات الإقليمية (APNIC، RIPE) | كابتشا صور | 10–20 استعلامًا |
| بوابات مزادات النطاقات | Cloudflare Turnstile | فحوصات نطاق سريعة |
| أدوات WHOIS المجمّعة | كابتشا مخصّص | بعد حد الطبقة المجانية |
سيناريو من السوق: مراقبة محفظة نطاقات
تخيّل وكالة رقمية في الرياض تدير محفظة تتجاوز 200 نطاقًا لعملائها، بينها نطاقات .sa و.com. يحتاج الفريق إلى فحص تواريخ التجديد أسبوعيًا حتى لا ينتهي نطاق عميل بالخطأ. عند فحص هذا العدد يدويًا تظهر reCAPTCHA v2 بسرعة على بوابة المُسجِّل فيتعطّل العمل ويضطر أحدهم إلى حلّها يدويًا مرة تلو الأخرى. بأتمتة الفحص وتفويض حل الكابتشا إلى CaptchaAI، يتحوّل الأمر من مهمة يدوية متعبة إلى تقرير أسبوعي تلقائي يعرض النطاقات التي تنتهي خلال 30 يومًا. المنطق نفسه ينطبق على فريق تطوير يبحث عن اسم متاح لمشروع جديد فيفحص عشرات البدائل دفعة واحدة.
بحث WHOIS مع حل reCAPTCHA v2 في Python
الفئة التالية تغلّف الدورة كاملة: تجلب صفحة WHOIS، وتفحص محتواها بحثاً عن reCAPTCHA، وتستخرج مفتاح الموقع، وترسله إلى نقطة النهاية in.php، ثم تستطلع res.php حتى يجهز الرمز، وأخيراً تعيد إرسال الطلب مع g-recaptcha-response وتحلّل الحقول المهمة مثل المُسجِّل وتاريخ الانتهاء وحالة النطاق. الدالتان bulk_lookup وcheck_availability توسّعان النمط نفسه إلى قوائم نطاقات كاملة مع تأخير بين الطلبات.
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، تنفّذ الفئة التالية نمط مراقبة مستمر: تضيف نطاقات إلى قائمة مراقبة، وتفحص كل واحد منها، وتحسب الأيام المتبقية حتى انتهاء الصلاحية، وتُبلّغ عن أي نطاق يقترب من التجديد خلال 30 يومًا. تحتفظ الدالة بسجلّ تاريخي لكل نطاق لمقارنة الحالات عبر الزمن، وتتعامل مع reCAPTCHA بالمنطق نفسه: اكتشاف، إرسال مفتاح الموقع، استطلاع، ثم إعادة الإرسال بالرمز.
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`));
تقليل ظهور الكابتشا وضبط الإيقاع
كل استعلام تتجنّب فيه تشغيل الكابتشا هو استعلام أسرع وأقل تكلفة. أفضل استراتيجية ليست حلّ عدد أكبر من الكابتشا بل تقليل عدد ما يظهر منها أصلاً عبر ضبط الإيقاع والتخزين المؤقت وتوزيع الحمل.
| الاستراتيجية | الفائدة |
|---|---|
| تخزين النتائج محليًا في ذاكرة مؤقتة | تجنّب تكرار البحث عن النطاق نفسه |
| اعتماد تأخير 3–5 ثوانٍ بين الطلبات | خفض معدل تشغيل الكابتشا |
| التدوير بين عدة بوابات WHOIS | توزيع الحمل على أكثر من مزوّد |
| ثبات الجلسة | الحفاظ على حالة اجتياز الكابتشا |
معالجة الأخطاء الشائعة
| المشكلة | السبب المحتمل | الإجراء |
|---|---|---|
| ظهور الكابتشا بعد ثلاثة استعلامات فقط | تحديد معدل الطلبات على البوابة | زد التأخير واستخدم خوادم وسيطة سكنية |
| WHOIS يعيد "No match" | حجب البيانات عبر RDAP أو حماية الخصوصية | جرّب بوابة WHOIS بديلة أو استعلم المُسجِّل مباشرة |
| رفض رمز reCAPTCHA عند الإرسال | انتهاء صلاحية الرمز قبل إرساله | احرص على الحل والإرسال خلال أقل من دقيقتين وداخل الجلسة نفسها |
| حظر عنوان IP | تجاوز الحد اليومي للاستعلامات | دوّر الوكلاء السكنيين ووزّع الحمل زمنيًا |
أسئلة شائعة
هل أتمتة استعلامات WHOIS والتعامل مع reCAPTCHA v2 مسموحان؟
بيانات WHOIS عامة بطبيعتها، وأتمتة الاستعلام عنها لأغراض مشروعة مثل التحقق من توفّر الأسماء ومراقبة محفظتك من النطاقات ممارسة معتادة. التزم بشروط استخدام كل بوابة، واحترم حدود المعدل، ولا تحاول الوصول إلى بيانات محجوبة لا تملك صلاحية عليها. دور CaptchaAI هنا هو حل الكابتشا الذي يعترض سير عمل مصرّح به، لا أكثر.
كم تكلفة أتمتة استعلامات WHOIS باستخدام CaptchaAI؟
يعتمد CaptchaAI على تسعير قائم على الـ Threads وليس على الدفع لكل عملية حل. تبدأ خطة BASIC من 15 دولارًا شهريًا مع 5 threads وعدد غير محدود من عمليات الحل، وترتفع الخطط حتى ENTERPRISE بسعر 300 دولار مع 200 thread للأحمال الكبيرة. عدد الـ threads يحدّد كم استعلامًا يمكنك معالجته بالتوازي، بينما يبقى عدد عمليات الحل داخل الخطة غير محدود.
كيف أتعامل مع بوابات WHOIS التي تستخدم Cloudflare Turnstile بدل reCAPTCHA؟
بعض بوابات مزادات النطاقات تعرض Cloudflare Turnstile بدلاً من reCAPTCHA. يدعم CaptchaAI حل Turnstile كنوع متاح بشكل عام، ويظل النمط نفسه: اكتشف الكابتشا في الصفحة، أرسل مفتاح الموقع إلى CaptchaAI، ثم أعد إرسال الطلب بالرمز المحلول. راجع دليل حل Turnstile للاطلاع على التفاصيل الكاملة.
لماذا تظل بعض حقول WHOIS فارغة رغم حل الكابتشا؟
حل الكابتشا يفتح الصفحة، لكن كثيرًا من المُسجّلين يحجبون بيانات المُسجِّل (الاسم والبريد والهاتف) امتثالًا للوائح الخصوصية وبروتوكول RDAP. في هذه الحالة تحصل على الحقول العامة مثل اسم المُسجِّل وتواريخ الإنشاء والانتهاء وخوادم الأسماء، بينما تبقى بيانات المالك محجوبة. جرّب بوابة إقليمية مختلفة إذا كنت بحاجة إلى حقول إضافية متاحة قانونيًا.
الخطوات التالية
- البدء السريع مع CaptchaAI: حلّ أول كابتشا في 5 دقائق
- حل reCAPTCHA v2 عبر الـ API خطوة بخطوة
- حل Cloudflare Turnstile عبر الـ API
- حل GeeTest v3 عبر الـ API