فروشگاهی با ۵۰٬۰۰۰ محصول که در هر صفحه ۲۴ کارت نشان می‌دهد، حدود ۲٬۰۸۴ صفحه لیست دارد. اگر مکانیزم صفحه‌بندی را اشتباه بفهمید، duplicate جمع می‌کنید، رکورد از قلم می‌اندازید، یا منبع را با حلقه بی‌پایان تحت فشار می‌گذارید. این مقاله الگوها و شرط توقف را پوشش می‌دهد — مبنا در راهنمای وب‌اسکرپینگ.

خلاصه
  • اول Network tab: آیا لیست از API با page/cursor می‌آید؟
  • همیشه termination و سقف صفحه (safety cap) داشته باشید.
  • dedupe روی URL یا id، نه فقط شماره صفحه.

الگوهای pagination

  • page query — ?page=2 یا ?p=2
  • offset/limit — ?offset=24&limit=24 (مراقب drift اگر وسط لیست insert شود)
  • cursor — token در پاسخ JSON برای «صفحه بعد»؛ رایج در API مدرن
  • next link — تگ rel="next" یا لینک «بعدی» در HTML
  • load more — کلیک دکمه بدون تغییر URL
  • infinite scroll — محتوا با scroll به DOM اضافه می‌شود

API-backed pagination

همان دادهٔ UI اغلب از XHR با page یا cursor می‌آید. consume JSON معمولاً پایدارتر از پارس کارت HTML است — با همان session و احراز هویت مرورگر در صورت نیاز. ارتباط با دریافت از API و انتخاب API در برابر scrape.

مثال فروشگاه: ۵۰k محصول، ۲۴ در صفحه

برنامه‌ریزی: checkpoint (آخرین page پردازش‌شده)، ETA بر اساس rate limit، و ذخیره incremental تا crash وسط run همه را از بین نبرد. بعد از اتمام، نرمال‌سازی و dedupe روی SKU.

مشکلات رایج

  • Duplicate: sort پیش‌فرض عوض شد → همان محصول در صفحات مختلف.
  • Missing records: توقف زود هنگام «صفحه خالی» وقتی واقعاً خطای موقت بود.
  • Ordering: بین runها ترتیب عوض شد — کلید یکتا id است نه موقعیت در لیست.
  • Termination: حلقه بی‌پایان اگر شرط break اشتباه باشد.
seen_ids = set()
page = 1
MAX_PAGES = 2500  # safety: 50k/24 + حاشیه

while page <= MAX_PAGES:
    data = fetch_list(page=page)
    items = data["items"]
    if not items:
        break
    new_items = [i for i in items if i.id not in seen_ids]
    if not new_items and items:
        break  # تکرار کامل — احتمال پایان یا sort bug
    for i in new_items:
        seen_ids.add(i.id)
        save(i)
    if not data.get("has_next"):
        break
    page += 1

شبه‌کد — همیشه page و count را لاگ کنید؛ برای cursor به‌جای page += 1 token بعدی را از JSON بگیرید.

Infinite scroll و load more

در Playwright: scroll پله‌ای (مثلاً ۸۰٪ viewport)، wait_for_selector برای کارت جدید یا expect_response برای همان API لیست. سقف scroll iteration بگذارید. load more = کلیک دکمه + wait مشابه.

اشتباه رایج

فقط شمارش صفحه بدون dedupe — وقتی سایت صفحه آخر را duplicate نشان می‌دهد dataset آلوده می‌شود.

خطای ۴۲۹ در حلقه pagination: backoff و throttle. job شبانه: زمان‌بندی.

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

اول endpoint لیست در Network. اگر هست → JSON + cursor/page با checkpoint. اگر نیست → URL pagination با dedupe روی id. scroll/load more فقط وقتی API و next link در دسترس نیست — با Playwright و سقف iteration.