خانه/مقالات/اسکریپینگ مقیاس‌پذیر با Celery و RabbitMQ
برنامه نویسی
وب اسکریپینگ
پروکسی و چرخش IP
برگشت به مقاله‌ها

اسکریپینگ مقیاس‌پذیر با Celery و RabbitMQ

اسکریپینگ مقیاس‌پذیر با Celery و RabbitMQ
این مقاله به‌صورت مرحله‌به‌مرحله نشان می‌دهد چگونه با Celery و RabbitMQ یک سامانهٔ اسکریپینگ مقاوم و مقیاس‌پذیر بسازی؛ از نصب و پیکربندی گرفته تا تعریف تسک‌های پایتون، زمان‌بندی با Beat، اجرای on-demand و مانیتورینگ با Flower، همراه نکات عملی برای مدیریت retry، پروکسی، backoff و چالش‌های تولیدی.
آسان اسکریپ آسان اسکریپ
1405-06-23

آسان اسکریپ را در گوگل منبع ترجیحی کن تا مطالب ما زودتر و پررنگ‌تر برایت نمایش داده شود. افزودن از تنظیمات گوگل

مقدمه

اگر هنوز اسکریپ‌های کرون را شب‌ها بالا و پایین می‌بری، وقتش رسیده معماری‌ات را به مدل «وظیفه‌محور» منتقل کنی. در این راهنما یاد می‌گیری چگونه با ترکیب Celery و RabbitMQ یک سامانهٔ اسکریپینگ مقاوم، مانیتور‌شدنی و مقیاس‌پذیر بسازی. پس از خواندن این مقاله توانایی‌های زیر را خواهی داشت:

  • درک نقش پیام‌بروکر و صف‌ها در پردازش تسک‌ها
  • پیاده‌سازی یک اپ Celery ساده برای اسکریپ کردن صفحات وب
  • راه‌اندازی workerها، زمان‌بندی با Beat و مانیتورینگ با Flower
  • نکات عملی برای مدیریت نرخ، پروکسی، retry و پایداری در مقیاس

موقعیت استفاده: Cron یا Celery + RabbitMQ

قانون سرانگشتی: اگر فقط چند کار ساده و نادر داری، Cron کافی است؛ اما وقتی تعداد تسک‌ها یا نیاز به قابلیت‌های پیشرفته (ریزتری‌ها، مانیتورینگ، توزیع بار) افزایش پیدا کند، صف‌بندی مبتنی بر Celery به مراتب قابل‌اعتمادتر است.

  • Cron: ساده، آماده، اما بدون retry و دید مناسب برای خطاها.
  • Celery + RabbitMQ: خودکارسازی retry، backoff، توزیع وظایف و یک نقطهٔ مرکزی برای مانیتورینگ.

پیش‌نیازها

  • آشنایی با Python 3.7+
  • کتابخانه‌ها: Celery، kombu، requests، beautifulsoup4
  • RabbitMQ به‌عنوان message broker
  • ابزارهای مانیتورینگ مثل Flower و در محیط‌های بزرگ‌تر، راهکارهایی برای autoscaling (مثلاً Kubernetes)

Step 1: آماده‌سازی محیط

برای جدا نگه داشتن وابستگی‌ها بهتر است از virtual environment استفاده کنی:

python -m venv venv
source venv/bin/activate  # Linux/macOS
# یا در ویندوز:
# venv\Scripts\activate

سپس بسته‌های مورد نیاز را نصب کن:

pip install celery kombu requests beautifulsoup4

توضیح: Celery وظیفهٔ orkestration را بر عهده دارد، kombu لایهٔ ارتباطی با RabbitMQ است، و requests و BeautifulSoup برای دریافت و پارس HTML استفاده می‌شوند.

Step 2: نصب و نقش RabbitMQ

RabbitMQ صف‌ها را نگه‌داری می‌کند و پیام‌ها را بین کلاینت و worker منتقل می‌کند. جریان کلی این‌گونه است:

  1. کلاینت یک تسک (مثلاً «این URL را اسکریپ کن») به Celery می‌فرستد.
  2. Celery تسک را به RabbitMQ می‌سپارد و RabbitMQ آن را صف می‌کند.
  3. یکی از workerها تسک را برداشته و اجرا می‌کند.
  4. نتیجه ذخیره یا بازگردانده می‌شود (بسته به backend).

نمونهٔ دستورات نصب روی سیستم‌های مختلف:

# Ubuntu / Debian
sudo apt-get install curl gnupg apt-transport-https -y
sudo apt-get update -y
sudo apt-get install rabbitmq-server -y --fix-missing
sudo systemctl start rabbitmq-server

# macOS (Homebrew)
brew install rabbitmq
brew services start rabbitmq

# Windows (Chocolatey)
choco install rabbitmq
rabbitmq-service.bat start

برای اطمینان از اجرا بودن سرویس از فرمان مناسب سیستم‌عامل استفاده کن؛ مثلاً sudo systemctl status rabbitmq-server در لینوکس یا rabbitmqctl status در macOS.

Step 3: ایجاد اپ Celery

یک فایل پایتون بساز که پیکربندی Celery را نگه دارد، معمولاً نامش celery_config.py است. نمونهٔ ساده:

from celery import Celery

app = Celery('scraper', broker='pyamqp://guest@localhost//', backend='rpc://')

# قرار دادن تسک‌ها در صف پیش‌فرض 'scraping'
app.conf.task_default_queue = 'scraping'
app.conf.task_routes = {
    'tasks.scrape': {'queue': 'scraping'},
}

# برای autodiscover یا ثبت ماژول تسک‌ها
import tasks
app.autodiscover_tasks(['tasks'])

چه کاری انجام می‌دهد (شرح بخش‌ها):

  • Celery('scraper', broker=...): نمونه‌ای از اپ Celery می‌سازد و آن را به RabbitMQ متصل می‌کند.
  • backend='rpc://': اجازه می‌دهد نتایج تسک قابل بازیابی باشند (برای استفاده از result.get()).
  • task_default_queue و task_routes: صف پیش‌فرض و روتینگ را مشخص می‌کنند تا کنترل بار و جداسازی کارها ساده‌تر شود.
  • autodiscover_tasks: ماژول‌های وظیفه را بارگذاری می‌کند تا Celery بداند چه توابعی را می‌تواند اجرا کند.

Step 4: تعریف تسک‌های اسکریپینگ

یک فایل tasks.py بساز. ابتدا نمونهٔ بسیار ساده:

from celery_config import app
import requests
from bs4 import BeautifulSoup
from celery import shared_task

@shared_task
def scrape(url):
    response = requests.get(url, timeout=10)
    soup = BeautifulSoup(response.text, 'html.parser')
    return soup.title.text if soup.title else "No title found"

توضیح ورودی/خروجی و نقش تابع:

  • ورودی: url (رشتهٔ URL).
  • خروجی: متن تگ <title> یا پیام خطا به‌عنوان مقدار برگشتی تسک.
  • نقش: این تابع به‌عنوان یک تسک Celery ثبت شده و می‌تواند به‌صورت غیرهمزمان اجرا شود.

نسخهٔ بهبود یافته با retry و backoff و پشتیبانی از پروکسی:

from celery import shared_task
import requests
from bs4 import BeautifulSoup
import time

@shared_task(bind=True, max_retries=3, default_retry_delay=5)
def scrape(self, url, proxy=None):
    try:
        headers = {'User-Agent': 'Mozilla/5.0 (compatible; Scraper/1.0)'}
        proxies = {'http': proxy, 'https': proxy} if proxy else None
        resp = requests.get(url, timeout=15, headers=headers, proxies=proxies)
        resp.raise_for_status()
        soup = BeautifulSoup(resp.text, 'html.parser')
        return soup.title.text if soup.title else 'No title found'
    except requests.RequestException as exc:
        # مثال backoff ساده: افزایش تأخیر براساس تعداد تلاش‌هاn
        try:
            delay = min(60, 2 ** self.request.retries)
        except Exception:
            delay = 5
        raise self.retry(exc=exc, countdown=delay)

نکات مهم:

  • استفاده از bind=True تا بتوانیم از self.retry برای تلاش مجدد استفاده کنیم.
  • تنظیم max_retries و default_retry_delay برای جلوگیری از حلقه‌های بی‌نهایت.
  • اضافه کردن هدرها و گزینهٔ پروکسی برای کاهش ریسک بلوک شدن IP.

Step 5: اجرای Worker

برای شروع یک worker که فقط از صف scraping کار می‌گیرد:

celery -A celery_config worker --loglevel=info -Q scraping

توضیح پرچم‌ها:

  • -A celery_config: بارگذاری اپ از فایل مشخص.
  • worker: اجرای پردازش worker.
  • --loglevel=info: مقداردهی مناسب برای لاگ‌گیری توسعه و دیباگ.
  • -Q scraping: محدود کردن worker به صف موردنظر.

برای افزایش همزمانی می‌توان از --concurrency استفاده کرد:

celery -A celery_config worker --loglevel=info -Q scraping --concurrency=4

Step 6: زمان‌بندی با Celery Beat (اختیاری)

اگر می‌خواهی تسک‌ها به‌صورت دوره‌ای اجرا شوند، از Beat استفاده کن. در celery_config.py می‌توان برنامهٔ زمانی افزود:

from celery.schedules import crontab

app.conf.beat_schedule = {
    'scrape-every-hour': {
        'task': 'tasks.scrape',
        'schedule': crontab(minute=16),  # هر ساعت در دقیقهٔ 16
        'args': ('https://example.com',)
    },
}

سپس Beat را اجرا کن:

celery -A celery_config beat --loglevel=info

نکته: اگر schedule تغییر کند ممکن است نیاز باشد workerها را ری‌استارت کنی تا هماهنگ‌سازی کامل شود.

Step 7: اجرای تسک‌ها به‌صورت On-Demand

برای فراخوانی یک تسک از اسکریپت یا API بدون انتظار، از .delay() استفاده کن. نمونهٔ ساده:

from tasks import scrape

# enqueue و ادامهٔ کار بدون بلاک
result = scrape.delay('https://example.com')

# اگر نیاز به نتیجه داری، می‌توانی منتظر بمانی
print(result.get())

توضیح: delay یک پیام در صف قرار می‌دهد و کنترل را بلافاصله باز می‌گرداند؛ get() برای خواندن نتیجهٔ نهایی استفاده می‌شود (ترجیحاً در محیطی که انتظار بلوک شدن منطقی است).

Step 8: مانیتورینگ و اشکال‌زدایی

پایش فعال برای مقیاس واقعی حیاتی است. برخی فرمان‌ها و ابزارهای مفید:

# بررسی وضعیت تسک‌ها
celery -A celery_config inspect active
celery -A celery_config inspect scheduled
celery -A celery_config inspect reserved

# اجرا کردن Flower برای داشبورد
pip install flower
celery -A celery_config flower

با Flower می‌توانی وضعیت تسک‌ها، تعداد retryها، لاگ خطا و health هر worker را ببینی. همچنین برای تولیدی‌ها معیارهایی مثل طول صف، نرخ خطا و تاخیر هر تسک را مانیتور کن و آلارم تعریف کن.

ملاحظات تولیدی و رایج‌ترین مشکلات

  • Rate limits و بلوک شدن IP: از روتیشن پروکسی، تغییر User-Agent و تأخیر بین درخواست‌ها استفاده کن.
  • تسک‌های طولانی: تسک‌های پیچیده را به زیرتسک‌های کوچک تقسیم کن تا retry و timeout قابل مدیریت شود.
  • Memory leak: workerها را با استراتژی زمان‌بندی بازنشانی کن یا از مدل pre-fork با محدودیت حافظه استفاده کن.
  • Failover بروکر: در سیستم‌های حیاتی، RabbitMQ را کانفیگ کن که در خوشه اجرا شود تا single point of failure نداشته باشی.
  • Idempotency: تسک‌ها را طوری طراحی کن که اجرای مجدد آن‌ها مشکلی ایجاد نکند (مثلاً هنگام نوشتن در DB از upsert استفاده کن).

چک‌لیست مقیاس‌بندی (عملی)

  • صف‌بندی منطقی: جداسازی صف‌ها بر اساس نوع سایت یا اولویت.
  • بافرینگ و batching: اجرای 50k تسک یک‌باره ممنوع—تسک‌ها را برحسب زمان توزیع کن.
  • استراتژی retry و backoff: از exponential backoff برای شکست‌های موقتی استفاده کن.
  • مانیتورینگ و alerting: طول صف، نرخ خطا و زمان اجرای تسک‌ها را دنبال کن.
  • ذخیرهٔ نتایج: نتایج بزرگ را سریعاً به DB یا ذخیرهٔ خارجی ارسال کن تا حافظه worker آزاد بماند.
  • آزمون سیاه/مسیریابی: سناریوهای خطا را شبیه‌سازی و رفتار retry و DLQ را بررسی کن.

جمع‌بندی

ترکیب Celery و RabbitMQ مسیر مشخصی برای ساخت یک پلتفرم اسکریپینگ مقیاس‌پذیر و قابل‌اعتماد فراهم می‌کند: توزیع کار، retry خودکار، زمان‌بندی کدمحور و مانیتورینگ زمان‌واقعی. با رعایت اصولی مثل idempotency، تقسیم تسک‌ها، و استفاده از پروکسی و backoff، می‌توانی از دردسرهای کرون شبانه خلاص شوی و سیستم خود را به‌صورت ایمن و قابل رشد مدیریت کنی.

حالا یک قدم عملی بردار: یک worker محلی راه‌اندازی کن، یک تسک نمونه enqueue کن، و لاگ‌ها را با Flower زیر نظر بگیر تا تفاوت بین اسکریپ کردن دستی و پلتفرم مبتنی بر صف را احساس کنی.

آسان اسکریپ را در گوگل به‌عنوان منبع ترجیحی انتخاب کن

مقاله‌های جدید ما زودتر و پررنگ‌تر در نتایج گوگل و Discover برایت نمایش داده می‌شود. افزودن از تنظیمات گوگل

مطالب مرتبط

مقاله‌های مرتبط