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

تجميع الأخبار الآلي والتعامل مع اختبارات CAPTCHA

لبناء خط تجميع أخبار ثابت، اتبع ترتيبًا واحدًا: اجمع العناوين والروابط أولًا، ثم اسحب نص المقال فقط للعناوين التي تهم فريقك، وفوّض حل أي اختبار CAPTCHA يعترض الطريق إلى CaptchaAI. هذا التسلسل يقلّل عدد الطلبات إلى كل موقع، ويخفّض احتمال الحظر، ويبقي المسار قابلًا للتكرار يومًا بعد يوم.

الجهات التي تحتاج هذا المسار معروفة: مكاتب رصد الإعلام، وشركات العلاقات العامة، والفرق البحثية التي تتابع تغطية موضوع أو علامة تجارية عبر عشرات المصادر. تصوّر مكتب رصد إعلامي في القاهرة أو الرياض يتابع تغطية أحد عملائه عبر المنافذ الإقليمية العربية ووكالات الأنباء الدولية في آن واحد؛ التحدي الحقيقي هنا ليس حل CAPTCHA فحسب، بل ضبط إيقاع الجمع لكل نوع مصدر كي لا تُحظر ولا تفوتك التحديثات المهمة.

ما اختبارات CAPTCHA التي تواجهها على المواقع الإخبارية؟

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

نوع المصدر اختبار CAPTCHA المحفّز المحتوى المستهدف
منافذ الأخبار الكبرى Cloudflare Turnstile كشف الروبوتات المقالات والعناوين
وكالات الأنباء (أ ف ب، رويترز) reCAPTCHA v2 الوصول بالجملة الأخبار العاجلة
منشورات خلف جدار الدفع reCAPTCHA v3 محاولات الوصول المتكررة مقالات مميزة
المواقع الإخبارية المحلية reCAPTCHA v2 تحديد معدل الطلبات الأخبار الإقليمية
مجمّعات الأخبار Cloudflare Challenge كشف جمع البيانات الخلاصات المجمّعة
مواقع النشرات الصحفية صورة تحقق (Image CAPTCHA) صفحات التحميل محتوى العلاقات العامة

يغطّي CaptchaAI هذه الأنواع كلها: reCAPTCHA v2 وv3، وCloudflare Turnstile وChallenge، إضافة إلى اختبارات الصور. أما اختبارات hCaptcha وFunCaptcha فهي غير مدعومة حاليًا، لذا تجنّب المصادر التي تعتمد عليها في تصميم مسارك أو خصّها بمعالجة يدوية.

كيف تضبط وتيرة الجمع حسب نوع المصدر؟

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

نوع المصدر وتيرة مقترحة للمتابعة ملاحظة تشغيلية
الأخبار العاجلة والتقنية كل 5 إلى 15 دقيقة أرسل عددًا قليلًا من الطلبات مع تدوير الخادم الوسيط عند الحاجة
أقسام الصحف العامة كل 15 إلى 30 دقيقة اجمع العناوين أولًا ثم اسحب المقالات المطابقة فقط
المواقع المحلية أو الإقليمية كل 30 إلى 60 دقيقة راقب حدود المعدل لأنها أقل تحمّلًا للزحف الكثيف
النشرات الصحفية والبيانات الإعلانية كل 60 دقيقة أو عند الحاجة الأولوية هنا للاكتمال لا للسرعة

يحاسب CaptchaAI على أساس عدد الـ threads المتزامنة لا على أساس كل عملية حل، مع عمليات حل غير محدودة لكل thread خلال الشهر. لمتابعة عدد محدود من المصادر تكفي خطة STANDARD ($30 شهريًا، 15 thread)؛ ولأحمال أكبر مع عشرات المصادر المتوازية انتقل إلى ADVANCE ($90 شهريًا، 50 thread). راجع صفحة الأسعار على captchaai.com للاطّلاع على القيم المحدّثة.

بناء مُجمِّع الأخبار بلغة Python

الفئة NewsAggregator تنفّذ الترتيب الذي بدأنا به: تفتح الجلسة، وتجمع العناوين، وتكتشف وجود CAPTCHA في الصفحة، وتستدعي CaptchaAI للحل ثم تعيد إرسال الطلب. لاحظ أنها تفصل بين جمع العناوين (collect_headlines) وسحب نص المقال كاملًا (get_article)، وهذا الفصل هو ما يبقي عدد الطلبات منخفضًا.

import requests
import time
import re
from bs4 import BeautifulSoup
from datetime import datetime
import json

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("Timeout")


class NewsAggregator:
    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 collect_headlines(self, source_url, section=None):
        """Collect headlines from a news source."""
        url = f"{source_url}/{section}" if section else source_url
        resp = self.session.get(url, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, url)

        soup = BeautifulSoup(resp.text, "html.parser")
        articles = []

        for item in soup.select("article, .story, .headline-item, h2 a, h3 a"):
            link = item if item.name == "a" else item.select_one("a")
            if link:
                articles.append({
                    "title": link.get_text(strip=True),
                    "url": self._abs_url(source_url, link.get("href", "")),
                    "source": source_url,
                    "collected_at": datetime.now().isoformat(),
                })

        return articles

    def get_article(self, article_url):
        """Fetch full article content."""
        resp = self.session.get(article_url, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, article_url)

        soup = BeautifulSoup(resp.text, "html.parser")

        # Remove unwanted elements
        for tag in soup.select("script, style, nav, footer, .ad, .sidebar"):
            tag.decompose()

        content_el = soup.select_one(
            "article, .article-body, .story-body, .entry-content"
        )

        return {
            "url": article_url,
            "title": self._text(soup, "h1, .article-title"),
            "author": self._text(soup, ".author, .byline, [rel='author']"),
            "date": self._text(soup, "time, .publish-date, .article-date"),
            "content": content_el.get_text(separator="\n", strip=True) if content_el else "",
            "word_count": len(content_el.get_text().split()) if content_el else 0,
        }

    def aggregate_sources(self, sources, max_articles_per=20):
        """Aggregate headlines across multiple sources."""
        all_articles = []

        for source in sources:
            try:
                articles = self.collect_headlines(source["url"], source.get("section"))
                all_articles.extend(articles[:max_articles_per])
                print(f"{source['name']}: {len(articles)} headlines")
            except Exception as e:
                print(f"{source['name']}: Error - {e}")
            time.sleep(3)

        return all_articles

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

    def _solve_and_retry(self, html, url):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if not match:
            return self.session.get(url)
        sitekey = match.group(1)
        if 'cf-turnstile' in html:
            token = solve_captcha("turnstile", sitekey, url)
            return self.session.post(url, data={"cf-turnstile-response": token})
        token = solve_captcha("userrecaptcha", sitekey, url)
        return self.session.post(url, data={"g-recaptcha-response": token})

    def _text(self, soup, selector):
        el = soup.select_one(selector)
        return el.get_text(strip=True) if el else ""

    def _abs_url(self, base, href):
        if href.startswith("http"):
            return href
        return base.rstrip("/") + "/" + href.lstrip("/")


# Usage
aggregator = NewsAggregator(
    proxy="http://user:pass@residential.proxy.com:5000"
)

sources = [
    {"name": "Tech News A", "url": "https://technews-a.example.com", "section": "latest"},
    {"name": "Business B", "url": "https://business-b.example.com", "section": "tech"},
    {"name": "Industry C", "url": "https://industry-c.example.com"},
]

headlines = aggregator.aggregate_sources(sources)
print(f"Total: {len(headlines)} headlines collected")

مراقبة الأخبار حسب الكلمات المفتاحية

بعد أن يعمل المُجمِّع، تصبح المراقبة الموضوعية مسألة تصفية. الفئة NewsMonitor تبني فوق المُجمِّع: تمسح المصادر دوريًا، وتطابق العناوين مع قائمة كلماتك المفتاحية، وتحتفظ بمجموعة الروابط التي سبق أن رأتها كي لا تكرّر التنبيهات. هذا مفيد لفريق رصد يتابع اسم علامة تجارية أو ملفًا موضوعيًا واحدًا عبر مصادر متعددة.

class NewsMonitor:
    def __init__(self, keywords, sources, proxy=None):
        self.keywords = [kw.lower() for kw in keywords]
        self.aggregator = NewsAggregator(proxy=proxy)
        self.sources = sources
        self.seen_urls = set()

    def scan(self):
        """Scan for articles matching keywords."""
        headlines = self.aggregator.aggregate_sources(self.sources)
        matches = []

        for article in headlines:
            if article["url"] in self.seen_urls:
                continue

            title_lower = article["title"].lower()
            matched_kws = [kw for kw in self.keywords if kw in title_lower]

            if matched_kws:
                article["matched_keywords"] = matched_kws
                matches.append(article)
                self.seen_urls.add(article["url"])

        return matches

    def continuous_monitor(self, interval_min=30):
        """Run continuous monitoring with alerts."""
        while True:
            matches = self.scan()
            if matches:
                print(f"\n=== {len(matches)} new matches found ===")
                for m in matches:
                    print(f"  [{', '.join(m['matched_keywords'])}] {m['title']}")
                    print(f"    {m['url']}")
            else:
                print(f"No new matches at {datetime.now().strftime('%H:%M')}")

            time.sleep(interval_min * 60)


# Monitor for specific topics
monitor = NewsMonitor(
    keywords=["captcha", "bot detection", "web scraping", "automation"],
    sources=sources,
    proxy="http://user:pass@residential.proxy.com:5000",
)
matches = monitor.scan()

حل المشكلات المتكررة

معظم أعطال خطوط التجميع تنحصر في خمسة أنماط. هذا الجدول يربط كل عرض بسببه وإجرائه:

المشكلة السبب الإجراء
Cloudflare يحظر جميع الطلبات كشف روبوتات صارم استخدم الخادم الوسيط السكني مع User-Agent واقعي
ظهور جدار الدفع بدل المقال المحتوى خلف اشتراك مدفوع اكتشف جدار الدفع وضع عليه علامة أو تجاهله
CAPTCHA في كل صفحة تم وضع علامة على عنوان IP دوّر الخادم الوسيط وأضف تأخيرات تتجاوز 5 ثوانٍ
محتوى المقال فارغ المحتوى يُعرض عبر JS استخدم Selenium أو Puppeteer لمواقع SPA
مقالات مكررة القصة نفسها من مصادر متعددة أزل التكرار حسب تشابه العناوين

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

هل يدعم CaptchaAI جميع أنواع الحماية على المواقع الإخبارية؟

يحل CaptchaAI اختبارات reCAPTCHA v2 وv3، وCloudflare Turnstile وChallenge، واختبارات الصور، وهذه هي الأنواع الأكثر شيوعًا على المنافذ الإخبارية. أما hCaptcha وFunCaptcha فغير مدعومين حاليًا، فخطّط لمصادرك على هذا الأساس.

كم مصدرًا يمكن متابعته في الوقت نفسه؟

يتحدّد العدد بحسب عدد الـ threads في خطتك، إذ يمثّل كل thread طلب حل واحدًا قيد التنفيذ. متابعة بضعة مصادر بإيقاع معتدل تناسبها خطة STANDARD (15 thread)، بينما تحتاج عشرات المصادر المتوازية إلى ADVANCE (50 thread) أو أعلى.

ما الفرق بين جمع العناوين وسحب المقال الكامل؟

جمع العناوين خفيف: طلب واحد لكل صفحة قسم يعيد قائمة روابط. أما سحب المقال فيفتح كل رابط على حدة ويستخرج النص والكاتب والتاريخ. اجمع العناوين أولًا واسحب المقالات المطابقة لكلماتك المفتاحية فقط كي تبقى الطلبات قليلة.

ما نوع الخادم الوسيط الأنسب للمواقع الإخبارية؟

الخوادم الوسيطة السكنية المتناوبة هي الأنسب. تعتمد المنافذ الكبرى على Cloudflare الذي يحظر عناوين مراكز البيانات بصرامة، بينما تمرّ عناوين IP السكنية بمعدل نجاح أعلى.

أدلة ذات صلة


ابنِ سير عمل رصد إخباري أكثر ثباتًا مع CaptchaAI: ابدأ بجمع العناوين وحدها، ثم وسّع السحب إلى المقالات التي تطابق كلماتك المفتاحية أو أولويات فريقك.

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