حالات الاستخدام

تجريف لوحات الوظائف وحل اختبارات CAPTCHA عبر CaptchaAI

المشكلة التي تُوقف أي عملية جمع لبيانات الوظائف واحدة: بمجرد أن تلتقط لوحة مثل Indeed أو LinkedIn أو Glassdoor نمط وصول آلياً، تعرض اختبار CAPTCHA فتتوقف الجلسة. الحل عملي ومباشر — تمرّر الاختبار إلى CaptchaAI الذي يحلّ reCAPTCHA v2 وv3 وCloudflare Turnstile عبر الـ API، فيستأنف سكربت الاستخراج عمله بالرمز المُعاد دون تدخل يدوي.

هذا الدليل موجّه إلى:

  • فرق التوظيف التي تبني قواعد وظائف محدَّثة باستمرار
  • محللي سوق العمل الذين يرصدون مؤشرات الطلب والرواتب
  • أدوات وتطبيقات الموارد البشرية التي تجمع البيانات على نطاق واسع

الأمثلة بلغة Python، والمبدأ نفسه ينطبق على أي بيئة تُرسل طلبات HTTP من مصادر عالمية وإقليمية على حد سواء.


كيف يندمج CaptchaAI في مسار الاستخراج

قبل بناء السكربت، اضبط المنطق الذي يتعامل مع الاختبار عند ظهوره. يتبع التكامل أربع خطوات ثابتة لأي نوع من أنواع CAPTCHA:

  1. اكتشف الاختبار في صفحة النتيجة — ابحث عن data-sitekey أو وسم cf-turnstile أو g-recaptcha.
  2. أرسل المهمة إلى in.php مع طريقة الحل الصحيحة (userrecaptcha أو turnstile) ومفتاح الموقع وعنوان الصفحة، واحفظ معرّف المهمة.
  3. استطلع النتيجة من res.php كل بضع ثوانٍ حتى يعود الرمز المحلول بدل CAPCHA_NOT_READY.
  4. أعد إرسال الطلب بالرمز في الحقل المناسب — g-recaptcha-response لـ reCAPTCHA وcf-turnstile-response لـ Turnstile — لتحصل على الصفحة الكاملة.

هذا هو المنطق نفسه المُضمَّن في الدالة solve_captcha والصنف JobBoardScraper أدناه، حيث يتولى _solve_and_retry التمييز بين Turnstile وreCAPTCHA تلقائياً.


أنواع اختبارات CAPTCHA على أبرز لوحات الوظائف

النوع يحدد طريقة الإرسال إلى CaptchaAI وحقل الرمز الذي تعيد به الطلب، لذا اعرف ما تواجهه على كل منصة قبل التوسّع.

المنصة نوع التحقق المُحفّز البيانات المتاحة
Indeed reCAPTCHA v2 ارتفاع حجم الطلبات قوائم الوظائف والرواتب
LinkedIn Cloudflare Challenge كشف الروبوت الوظائف وبيانات الشركات
Glassdoor reCAPTCHA v2 كشف الاستخراج المراجعات والرواتب والوظائف
ZipRecruiter Cloudflare Turnstile الوصول الآلي قوائم الوظائف
Monster reCAPTCHA v2 صفحات البحث قوائم الوظائف
CareerBuilder reCAPTCHA v3 تسجيل الدخول والبحث قوائم الوظائف والسير الذاتية

اللافت أن معظم لوحات الوظائف تعتمد على عائلة reCAPTCHA أو Cloudflare، وكلها ضمن الأنواع التي يحلّها CaptchaAI. أما hCaptcha وFunCaptcha فغير مدعومَين حالياً، ونادراً ما تظهران في هذا المجال.


سكربت استخراج لوحة الوظائف مع معالجة CAPTCHA

import requests
import time
import re
from bs4 import BeautifulSoup

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY,
        "method": method,
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    }
    data.update(kwargs)
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]
    raise TimeoutError("Solve timeout")


class JobBoardScraper:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def search_jobs(self, base_url, query, location, pages=5):
        """Search job listings across multiple pages."""
        all_jobs = []

        for page in range(pages):
            url = f"{base_url}/jobs?q={query}&l={location}&start={page * 10}"
            resp = self.session.get(url, timeout=30)

            # Check for CAPTCHA
            if self._has_captcha(resp.text):
                resp = self._solve_and_retry(resp.text, url)

            if resp.status_code == 200:
                jobs = self._parse_listings(resp.text)
                all_jobs.extend(jobs)
                print(f"Page {page + 1}: {len(jobs)} jobs found")
            else:
                print(f"Page {page + 1}: Request failed ({resp.status_code})")

            time.sleep(3)  # Rate limit

        return all_jobs

    def _has_captcha(self, html):
        indicators = [
            'data-sitekey=',
            'g-recaptcha',
            'cf-turnstile',
            'captcha-delivery',
        ]
        return any(ind in html.lower() for ind in indicators)

    def _solve_and_retry(self, html, url):
        # Try reCAPTCHA first
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            sitekey = match.group(1)

            # Detect Turnstile vs reCAPTCHA
            if 'cf-turnstile' in html:
                token = solve_captcha("turnstile", sitekey, url)
                field = "cf-turnstile-response"
            else:
                token = solve_captcha("userrecaptcha", sitekey, url)
                field = "g-recaptcha-response"

            return self.session.post(url, data={field: token})

        return self.session.get(url)

    def _parse_listings(self, html):
        soup = BeautifulSoup(html, "html.parser")
        jobs = []

        for card in soup.select(".job_seen_beacon, .jobsearch-ResultsList > li"):
            title_el = card.select_one("h2 a, .jobTitle a")
            company_el = card.select_one(".companyName, [data-testid='company-name']")
            location_el = card.select_one(".companyLocation, [data-testid='text-location']")
            salary_el = card.select_one(".salary-snippet, .estimated-salary")

            if title_el:
                jobs.append({
                    "title": title_el.get_text(strip=True),
                    "company": company_el.get_text(strip=True) if company_el else "",
                    "location": location_el.get_text(strip=True) if location_el else "",
                    "salary": salary_el.get_text(strip=True) if salary_el else "",
                    "url": title_el.get("href", ""),
                })

        return jobs


# Usage
scraper = JobBoardScraper(
    proxy="http://user:pass@residential.proxy.com:5000"
)
jobs = scraper.search_jobs(
    base_url="https://jobs.example.com",
    query="python developer",
    location="New York",
    pages=10,
)
print(f"Total jobs collected: {len(jobs)}")

الصنف يفحص كل صفحة نتائج بحثاً عن CAPTCHA قبل التحليل، فإن وُجد يحلّه ويعيد الطلب، ثم يستخرج العنوان والشركة والموقع والراتب من كل بطاقة وظيفة.


جمع بيانات الرواتب لتحليل السوق

بمجرد أن يعمل الاستخراج، يمكنك المرور على مجموعة من المسميات الوظيفية والمواقع لبناء قاعدة رواتب مرجعية. الدالة التالية تسجّل لكل مجموعة:

  • عدد القوائم المُستخرجة لكل مسمّى وموقع
  • عدد القوائم التي تتضمن نطاق راتب
  • عيّنات من الرواتب المرصودة

ثم تحفظ النتائج في ملف CSV جاهز للتحليل:

import csv


def collect_salary_data(titles, locations, output_file):
    """Collect salary data across job titles and locations."""
    scraper = JobBoardScraper(
        proxy="http://user:pass@residential.proxy.com:5000"
    )

    results = []
    for title in titles:
        for location in locations:
            try:
                jobs = scraper.search_jobs(
                    "https://jobs.example.com",
                    title, location, pages=3,
                )
                salaries = [j["salary"] for j in jobs if j["salary"]]
                results.append({
                    "title": title,
                    "location": location,
                    "listings": len(jobs),
                    "with_salary": len(salaries),
                    "salary_samples": "; ".join(salaries[:5]),
                })
                time.sleep(5)
            except Exception as e:
                results.append({
                    "title": title,
                    "location": location,
                    "error": str(e),
                })

    with open(output_file, "w", newline="") as f:
        writer = csv.DictWriter(
            f, fieldnames=["title", "location", "listings",
                           "with_salary", "salary_samples", "error"],
        )
        writer.writeheader()
        writer.writerows(results)

    return results


# Collect salary data for market analysis
collect_salary_data(
    titles=["Data Engineer", "ML Engineer", "DevOps Engineer"],
    locations=["San Francisco", "New York", "Austin", "Remote"],
    output_file="salary_data.csv",
)

سيناريو عملي: مقارنة رواتب التقنية بين أسواق الخليج والأسواق العالمية

لنفترض أن فريق موارد بشرية في شركة تقنية بالخليج يريد بناء مؤشر رواتب لمقارنة عروضه بالسوق. يمرّ الفريق على مسميات مثل «Data Engineer» و«ML Engineer» عبر مواقع عالمية وإقليمية، ويقارن نطاقات الرواتب بين الرياض ودبي ولندن وريموت. مع تكرار عمليات البحث، تبدأ اختبارات reCAPTCHA بالظهور على صفحات النتائج، فيتولى CaptchaAI حلّها ليبقى التدفق مستمراً.

على مستوى الحجم، يعتمد CaptchaAI على نموذج تسعير قائم على عدد الـ Threads المتزامنة مع حلول غير محدودة لكل Thread. باقة ADVANCE بسعر ‎$90 شهرياً (50 Thread) تكفي لمشروع مقارنة رواتب متوسط يعمل بضع ساعات يومياً، بينما تبدأ الباقات من BASIC بسعر ‎$15 شهرياً (5 Threads) للتجارب الأولية. الأسعار بالدولار الأمريكي ولا تتغير بحسب نوع الاختبار.

ملاحظة مهمة: اجمع فقط البيانات العامة التي يُسمح لك بالوصول إليها، والتزم بشروط استخدام كل منصة. هذا الدليل موجّه لأبحاث السوق المشروعة وأتمتة سير العمل المصرّح به، لا لتجاوز أي قيود على بيانات محمية أو خاصة.


نصائح إعداد الاستخراج على لوحات الوظائف

الهدف هو محاكاة سلوك تصفّح واقعي حتى تقلّ اختبارات CAPTCHA من الأساس، وتتركز مواردك على الحالات التي تظهر فعلاً.

التقنية لماذا تساعد
الوكلاء السكنيون الدوّارون يوزّعون الطلبات عبر عناوين IP حقيقية
تأخير 3–5 ثوانٍ بين الصفحات يحاكي سرعة التصفح البشري
وكيل مستخدم ثابت لكل جلسة يتجنب عدم تطابق البصمة
قبول ملفات تعريف الارتباط تتتبّع لوحات الوظائف الجلسات عبرها
ترتيب عمليات البحث عشوائياً يتجنب أنماط الصفحات المتسلسلة
حدّ أقصى 200 صفحة/يوم لكل نطاق يبقيك تحت عتبات الكشف

استكشاف الأخطاء وحلّها

المشكلة السبب الإجراء
CAPTCHA في كل عملية بحث تمّ وسم عنوان IP أو تجاوز المعدل بدّل IP وأضف تأخيرات أطول
صفحة نتائج فارغة عادت كتلة CAPTCHA بدل المحتوى اكشف الاختبار قبل التحليل
«يرجى التحقق من أنك إنسان» تفعّل كشف الروبوت استخدم وكيلاً سكنياً + وكيل مستخدم واقعي
يلزم تسجيل الدخول لبيانات الراتب المنصة تحجب المحتوى نفّذ جلسة مصادَقاً عليها
نتائج مختلفة عن المتصفح اختلاف الموقع الجغرافي أو ملفات الارتباط طابِق Accept-Language والوكيل الجغرافي

الأسئلة الشائعة

أي أنواع CAPTCHA على لوحات الوظائف يحلّها CaptchaAI؟

يغطي CaptchaAI عائلة reCAPTCHA v2 وv3 وCloudflare Turnstile وCloudflare Challenge — وهي الأنواع الشائعة على Indeed وGlassdoor وZipRecruiter وLinkedIn. أما hCaptcha وFunCaptcha فغير مدعومَين حالياً.

كيف أميّز نوع الاختبار برمجياً قبل الحل؟

افحص محتوى الصفحة: وجود cf-turnstile يشير إلى Cloudflare Turnstile، بينما g-recaptcha أو data-sitekey مع نطاق google يشير إلى reCAPTCHA. الدالة _solve_and_retry في المثال تقوم بهذا التمييز وتختار طريقة الإرسال المناسبة تلقائياً.

هل يكفي حل CAPTCHA وحده لاستقرار الاستخراج؟

لا؛ حل الاختبار يعالج الحاجز اللحظي، لكن استقرار الجمع على المدى الطويل يتطلب أيضاً وكلاء سكنيين دوّارين وتأخيرات واقعية وحدّاً يومياً للطلبات. اجمع الاثنين معاً لتقليل ظهور الاختبارات أصلاً.

هل الوصول الآلي إلى لوحات الوظائف مسموح؟

تختلف الشروط بين منصة وأخرى، ومعظمها يقيّد الوصول الآلي بدرجات متفاوتة. التزم بجمع البيانات العامة المصرّح بها فقط، وراجع شروط الاستخدام قبل التوسع في أي مشروع.


أدلة ذات صلة


اجمع بيانات سوق العمل على نطاق واسع — احصل على مفتاح CaptchaAI لحل اختبارات CAPTCHA آلياً.

التعليقات غير مفعّلة لهذا المقال.