در بسیاری از سایت‌های مدرن، View Source تقریباً خالی است اما در مرورگر پر از محصول و قیمت می‌بینید. دلیلش رندر JavaScript است: HTML اولیه فقط اسکلت است و داده بعد از اجرای JS در DOM می‌نشیند. برای استخراج داده از وب در این حالت، Playwright مرورگر headless را کنترل می‌کند.

خلاصه
  • HTML اولیه ≠ آنچه کاربر می‌بیند
  • Playwright = مرورگر واقعی + API اتوماسیون
  • wait strategy مهم‌تر از سرعت خام
  • گاهی intercept Network از parse DOM بهتر است

چرا HTML اولیه کامل نیست؟

SPA (React/Vue) داده را با fetch می‌گیرد و در client رندر می‌کند. سرور ممکن است فقط <div id="app"></div> بدهد. Requests+BeautifulSoup روی همان پاسخ، قیمت را نمی‌بیند.

JavaScript rendering و DOM

مرورگر JS را اجرا می‌کند، DOM درختی ساخته می‌شود و ممکن است چند بار mutate شود. Scraper شما باید بعد از این فاز query بزند — نه روی HTML خام.

Page URL→Browser→JavaScript→ DOM→Extract→Structured Data
نمونه نمایشی — مسیر Playwright

Playwright چه می‌کند؟

Chromium/Firefox/WebKit را launch می‌کند، browser context (cookie، viewport) می‌سازد، صفحه را باز می‌کند و با locator روی DOM کار می‌کند. Auto-wait تا element آماده شود صبر می‌کند.

Wait strategy

  • wait_for_selector — وقتی عنصر محصول ظاهر شد
  • wait_until=domcontentloaded — سبک‌تر از networkidle
  • expect_response — منتظر API لیست محصول

Network requests

در DevTools ببینید آیا لیست از XHR می‌آید. intercept همان JSON اغلب پایدارتر از scrape کارت HTML است — با همان session و احراز هویت مرورگر در صورت نیاز.

انتخابگر، ورود، صفحه‌بندی

selector پایدار ترجیحاً data-*. session در context نگه دارید. pagination و infinite scroll در مقاله صفحه‌بندی.

Headless

بدون UI سریع‌تر است؛ بعضی سایت‌ها headless را تشخیص می‌دهند — throttle و رفتار منطقی مهم‌تر از مخفی‌کاری است.

چه زمانی Playwright لازم نیست؟

داده در HTML اولیه یا API رسمی. اول API در برابر Scraping را بررسی کنید.

محدودیت‌ها: سرعت، حافظه، پیچیدگی

هر browser context ده‌ها تا صدها مگابایت RAM می‌گیرد؛ ۵۰ tab هم‌زمان روی یک VPS کوچک معمولاً به swap و crash می‌خورد. pool worker محدود (مثلاً ۳–۵ context) و reuse context برای چند URL منطقی‌تر از launch برای هر لینک است.

سرعت: رندر JS + wait از یک GET ساده کندتر است — برای هزاران URL فقط وقتی Playwright لازم است استفاده کنید. deploy نیاز به dependency مرورگر و در Linux گاهی کتابخانه‌های سیستمی دارد. selector شکننده = هزینه نگهداری مداوم؛ ترجیح data-testid / API شبکه.

python">
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_context(locale="fa-IR").new_page()
    page.goto("https://example.com/shop", wait_until="domcontentloaded")
    page.wait_for_selector("[data-product-id]")
    for node in page.locator("[data-product-id]").all():
        print(node.get_attribute("data-product-id"))
    browser.close()

در عمل چه انتخابی کنیم؟

اگر در Network endpoint JSON لیست دارید و مجازید → API. اگر فقط UI → Playwright با wait مشخص و pool محدود worker. برای مقایسه ابزار مرورگر: Playwright یا Selenium. پیاده‌سازی: Scraper اختصاصی.