خانه/مقالات/اسکریپینگ صفحات جاوااسکریپت با Scrapy
برنامه نویسی
Playwright
برگشت به مقاله‌ها

اسکریپینگ صفحات جاوااسکریپت با Scrapy

اسکریپینگ صفحات جاوااسکریپت با Scrapy
این مقاله چهار راهکار رایج برای رندر کردن صفحات جاوااسکریپت با Scrapy را با جزئیات فنی، نمونه‌کد و نکات عملی مقایسه می‌کند؛ پیشنهاد اصلی برای اکثر پروژه‌ها استفاده از scrapy-playwright است، همراه با بهترین روش‌ها برای مدیریت منابع، خطاها و امنیت.
آسان اسکریپ آسان اسکریپ
1405-03-31

مقدمه

در سال‌های اخیر SPAها و صفحات سنگین Javascript (مثل React، Angular یا Vue) رایج شده‌اند و داده‌ها اغلب در سمت کلاینت رندر می‌شوند. وقتی HTML اولیه پاسخ شامل دادهٔ مورد نیاز نیست یا باید با صفحه تعامل داشته باشید (کلیک، اسکرول، پیمایش)، باید از مرورگر Headless برای رندر جاوااسکریپت استفاده کنید. در این مقاله با چهار روش اصلیِ ادغام مرورگرها با Scrapy آشنا می‌شوید: scrapy-playwright، scrapy-splash، scrapy-selenium و scrapy-pyppeteer. در پایان خواهید دانست کدام راهکار برای چه سناریویی مناسب‌تر است و چطور هر کدام را پیکربندی و استفاده کنید.

چرا از رندر سمت کلاینت استفاده کنیم و چه جایگزین‌هایی دارد

قبل از رندر کامل صفحه همیشه این گزینه‌ها را بررسی کنید:

  • آیا API داخلی یا endpointهای XHR وجود دارد که مستقیماً JSON بازگردانند؟ این همیشه سریع‌تر و پایدارتر از رندر کامل صفحه است.
  • آیا داده‌ها در HTML اولیه هستند (که با یک Request ساده قابل دریافت باشد)؟ اگر بله، از رندر استفاده نکنید.
  • اگر نیاز به تعامل (مثلاً کلیک برای لود محتوای بعدی) دارید، از یک راهکار رندر استفاده کنید.

در ادامه هر راهکار را قدم‌به‌قدم توضیح می‌دهم: نصب، پیکربندی در settings.py، نمونه‌ی Spider و نکات عملی مرتبط با performance، امنیت و پایداری.

Scrapy Playwright

ایدهٔ کلی: Playwright یک ابزار مدرن برای کنترل مرورگر (Chromium, Firefox, WebKit) است. scrapy-playwright یک دانلود هندلر برای Scrapy فراهم می‌کند تا به‌سادگی صفحات جاوااسکریپتی را رندر کنید.

مزایا:

  • پشتیبانی قوی از waiting (wait_for_selector)، intercepting، و contextها.
  • به‌روز و فعال؛ معمولاً عملکرد بهتر و API غنی‌تر نسبت به بقیه دارد.
  • قابلیت استفاده از پروکسی، اسکرین‌شات، PDF و مدیریت browser context برای جداسازی sessionها.

معایب و محدودیت‌ها: پشتیبانی رسمی روی Windows محدود است (می‌توان با WSL کار کرد). مصرف منابع و زمان راه‌اندازی مرورگر بالاتر از درخواست ساده است.

نصب و راه‌اندازی (دستورات):

pip install scrapy-playwright
playwright install

سپس در settings.py دو تنظیم اصلی لازم است: دانلود هندلر و ری‌اکتور Asyncio:

# settings.py
DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

مثال استفاده در Spider: برای فعال کردن رندر Playwright کافی‌ست در متای درخواست پرچم 'playwright': True بگذارید. ورودی‌ها: URL و meta. خروجی: یک response که بعد از رندر صفحه برمی‌گردد.

# spiders/quotes_playwright.py
import scrapy
from myproject.items import QuoteItem

class QuotesSpider(scrapy.Spider):
    name = 'quotes'

    def start_requests(self):
        url = 'https://quotes.toscrape.com/js/'
        yield scrapy.Request(url, meta={'playwright': True})

    def parse(self, response):
        # response حاوی HTML پس از اجرای جاوااسکریپت است
        for quote in response.css('div.quote'):
            item = QuoteItem()
            item['text'] = quote.css('span.text::text').get()
            item['author'] = quote.css('small.author::text').get()
            item['tags'] = quote.css('div.tags a.tag::text').getall()
            yield item

نکات عملی و بهترین روش‌ها:

  • از wait_for_selector یا Timeouts مشخص استفاده کنید تا از برگشتن HTML ناقص جلوگیری شود.
  • تنظیم browser contexts را برای جداسازی sessionها در scraping موازی در نظر بگیرید.
  • برای کاهش مصرف منابع، تنها درخواست‌هایی را که واقعاً نیاز به رندر دارند فعال کنید (تشخیص با قواعد URL یا بررسی اولیه محتوا).

Scrapy Splash

ایدهٔ کلی: scrapy-splash یک رندرر سبک مبتنی بر سرویس HTTP (Splash) است که از Lua برای کنترل مراحل رندر پشتیبانی می‌کند. شما یک سرویس Splash را (معمولاً در Docker) اجرا می‌کنید و Scrapy با ارسال درخواست به این سرویس، HTML رندرشده را دریافت می‌کند.

مزایا:

  • سبک‌تر از برخی راهکارها و برای بارهای متوسط مناسب است.
  • مستندات کامل و سابقهٔ استفاده طولانی در جامعهٔ Scrapy.

معایب:

  • نسبت به Playwright/Puppeteer قدیمی‌تر است و بعضی قابلیت‌های جدید را ندارد.
  • نیاز به مدیریت یک سرویس جداگانه (Docker) و نوشتن اسکریپت‌های Lua برای تعاملات پیشرفته.

راه‌اندازی سریع:

docker pull scrapinghub/splash
docker run -it -p 8050:8050 --rm scrapinghub/splash

معمولاً Splash روی http://localhost:8050 اجرا می‌شود. سپس پکیج scrapy-splash را نصب کنید:

pip install scrapy-splash

تنظیمات نمونه در settings.py:

# settings.py
SPLASH_URL = 'http://127.0.0.1:8050'

DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
SPIDER_MIDDLEWARES = {
    'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

نمونهٔ Spider با SplashRequest:

from scrapy_splash import SplashRequest

class QuotesSplashSpider(scrapy.Spider):
    name = 'quotes_splash'

    def start_requests(self):
        url = 'https://quotes.toscrape.com/js/'
        yield SplashRequest(url, callback=self.parse)

    def parse(self, response):
        # response پس از رندر Splash است
        ...

نکات عملی:

  • Lua scripting به شما امکان تعامل پیچیده (click، scroll با منطق سفارشی) قبل از گرفتن HTML را می‌دهد؛ اما منحنی یادگیری دارد.
  • برای افزایش سرعت می‌توانید بارگذاری تصاویر را غیرفعال کنید یا قوانین adblock تعریف کنید.
  • Splash برای مقیاس‌بندی نیاز به مدیریت کانتینرها و منابع دارد؛ Zyte/سرویس‌های میزبانی Splash گزینهٔ میزبانی را می‌دهند.

Scrapy Selenium

ایدهٔ کلی: Selenium ابزار شناخته‌شده‌ای برای اتوماسیون مرورگر است که با Scrapy از طریق scrapy-selenium یا به‌صورت دستی قابل ترکیب است. Selenium به‌خصوص زمانی مفید است که نیاز به تعاملات پیچیدهٔ UI دارید.

مزایا:

  • پایداری و جامعهٔ بزرگ؛ بسیاری از تست‌ها و ابزارها مبتنی بر Selenium هستند.
  • قابلیت کار با ران‌تایم‌ها و درایورهای مختلف (ChromeDriver، GeckoDriver).

معایب:

  • در مقایسه با Playwright/Puppeteer کندتر و سنگین‌تر است و scrapy-selenium در سال‌های اخیر کمتر نگهداری شده است.
  • نیاز به نصب و نگهداری driver (مثلاً chromedriver) و مطابقت نسخهٔ درایور با مرورگر.

نصب و پیکربندی پایه:

pip install scrapy-selenium

نمونهٔ تنظیمات برای ChromeDriver در settings.py:

from shutil import which

SELENIUM_DRIVER_NAME = 'chrome'
SELENIUM_DRIVER_EXECUTABLE_PATH = which('chromedriver')
SELENIUM_DRIVER_ARGUMENTS = ['--headless']

DOWNLOADER_MIDDLEWARES = {
    'scrapy_selenium.SeleniumMiddleware': 800,
}

نمونه Spider با SeleniumRequest:

from scrapy_selenium import SeleniumRequest

class QuotesSeleniumSpider(scrapy.Spider):
    name = 'quotes_selenium'

    def start_requests(self):
        url = 'https://quotes.toscrape.com/js/'
        yield SeleniumRequest(url=url, callback=self.parse)

    def parse(self, response):
        # response حاوی HTML پس از کنترل توسط Selenium است
        ...

نکات عملی:

  • همواره نسخهٔ مناسب Chrome/Chromedriver را هماهنگ کنید؛ به‌ازای آپدیت مرورگر، درایور باید بروز شود.
  • برای افزایش پایداری از مدیریت صریح Timeouts و try/except برای برخورد با خطاهای WebDriver استفاده کنید.
  • برای scrape موازی منابع بالایی نیاز است؛ Selenium عموماً برای اسکریپت‌های کم‌مقیاس یا تعاملات پیچیده مناسب‌تر است.

Scrapy Pyppeteer (scrapy-pyppeteer)

ایدهٔ کلی: Pyppeteer نسخهٔ پایتون Puppeteer است که امکان کنترل Chromium را فراهم می‌کند. scrapy-pyppeteer دانلود هندلر مخصوصی دارد که درخواست‌هایی با متا {"pyppeteer": True} را رندر می‌کند.

مزایا و معایب:

  • در گذشته محبوب بود اما اکنون وضعیت نگهداری ضعیف است و توصیهٔ عمومی استفاده از Scrapy Playwright است.
  • اگر با Pyppeteer آشنا هستید و پروژهٔ شما با آن کار می‌کند، می‌تواند گزینه‌ای باشد، ولی ریسک به‌روزرسانی و باگ‌های بدون پشتیبانی وجود دارد.

نصب پایه و تنظیمات:

pip install scrapy-pyppeteer
# settings.py
DOWNLOAD_HANDLERS = {
    "http": "scrapy_pyppeteer.handler.ScrapyPyppeteerDownloadHandler",
    "https": "scrapy_pyppeteer.handler.ScrapyPyppeteerDownloadHandler",
}
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

مثال استفاده در Spider:

class QuotesPyppeteerSpider(scrapy.Spider):
    name = 'quotes_pyppeteer'

    def start_requests(self):
        url = 'https://quotes.toscrape.com/js/'
        yield scrapy.Request(url=url, callback=self.parse, meta={"pyppeteer": True})

    def parse(self, response):
        ...

نکته مهم: چون این پروژه کمتر نگهداری می‌شود، قبل از استفادهٔ جدی تست کنید و در صورت امکان به جای آن از scrapy-playwright استفاده کنید.

مقایسهٔ عملی و نکات مربوط به عملکرد و امنیت

چند معیار مهم برای انتخاب:

  • پایداری و نگهداری: scrapy-playwright معمولاً بهترین تعادل بین قابلیت‌ها و نگهداری را دارد.
  • پیچیدگی راه‌اندازی: Splash نیاز به Docker و Lua دارد؛ Selenium نیاز به درایور؛ Playwright و Pyppeteer نصب ساده‌تری دارند اما Playwright عملکرد برتر دارد.
  • مصرف منابع: رندر کامل مرورگر هزینه‌بر است؛ سعی کنید فقط صفحات ضروری را رندر کنید یا از headless APIs / XHRها استفاده کنید.
  • مقیاس‌پذیری: برای اسکریپ‌های بزرگ، از pool مرورگر، browser contexts و سرویس‌های مدیریت‌شده (یا کلاستر کانتینری) استفاده کنید.
  • امنیت و قوانین: همواره قوانین سایت و فایل robots.txt را بررسی کنید؛ برای جلوگیری از بلاک شدن از پراکسی چرخشی، تغییر User-Agent و رعایت نرخ درخواست استفاده کنید.

نکات مربوط به خطاها و پایداری:

  • استفاده از Retries و Backoff برای خطاهای شبکه و timeouts.
  • در Playwright و Pyppeteer از timeouts مشخص و بررسی وجود selectorها قبل از استخراج استفاده کنید.
  • برای جلوگیری از memory leak در long-running spiders، دوره‌ای contextها/صفحه‌ها را ببندید.

الگوهای سودمند و بهترین روش‌ها

  • اولویت با API: همیشه قبل از رندر کردن به دنبال APIهای داخلی باشید.
  • Selective Rendering: تنها صفحات یا بخش‌هایی که نیاز به JS دارند را رندر کنید؛ از بررسی اولیهٔ HTML برای تصمیم‌گیری استفاده کنید.
  • کاهش حجمی رندر: تصاویر/فونت‌ها را غیرفعال کنید یا adblock اجرا کنید تا زمان و حافظه کاهش یابد (در Splash یا تنظیمات مرورگر).
  • مدیریت session: برای سایت‌هایی که به session وابسته‌اند از browser contexts استفاده کنید تا هر spider یا هر کارگر session جدا داشته باشد.
  • پایش و مانیتورینگ: metricهایی مثل latency رندر، خطاهای صفحه و نرخ موفقیت استخراج را مانیتور کنید تا بتوانید به‌موقع مقیاس‌دهی یا رفع خطا کنید.

جمع‌بندی

خلاصهٔ پیشنهادها:

  • برای اکثر پروژه‌های جدید و نیازمند به رندر جاوااسکریپت از scrapy-playwright شروع کنید؛ چون فعال، قدرتمند و با API غنی است.
  • اگر به سرویس سبک HTTP با کنترل Lua نیاز دارید یا سیستم شما از قبل با Splash سازگار است، scrapy-splash گزینهٔ مناسبی است.
  • برای تعاملات خیلی پیچیده یا وقتی که Selenium برایتان حیاتی است، از scrapy-selenium استفاده کنید ولی از مشکلات نگهداری و منابع آگاه باشید.
  • از scrapy-pyppeteer تنها در صورت آشنایی قبلی و با احتیاط استفاده کنید؛ ایده‌آل‌تر است که به Playwright مهاجرت کنید.

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

مطالب مرتبط

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