مقدمه
اسکریپ صفحات و تعامل با المانها معمولاً نیاز دارد که آن المانها در نمای صفحه قابل مشاهده باشند. توانایی اسکرول کردن صفحه یکی از ابزارهای پایهای در جعبهابزار وب اسکریپینگ است—برای پیدا کردن عناصر، بارگذاری تصاویر lazy، گرفتن اسکرینشات یا شبیهسازی رفتار کاربر. در این راهنما، با متدهای مختلف اسکرول در Playwright آشنا میشویم، نمونهکدهای پایتون (async) و توضیحات خطبهخط میآوریم و نکات عملی دربارهٔ پایداری، کارآیی و امنیت را پوشش میدهیم.
روشها برای اسکرول با Playwright
- اسکرول به یک عنصر مشخص
- اسکرول به اندازهٔ مشخص (pixels)
- اسکرول تا ته صفحه
- اسکرول با میانبرهای صفحهکلید
- اسکرول با ماوس (wheel)
- شبیهسازی لمس/تپ (touchscreen)
روش ۱: اسکرول به عنصر مشخص
ایدهٔ کلی: المان را پیدا کنید، آن را بهنمایش بیاورید (scroll into view) و سپس با آن تعامل کنید. مزیت این روش دقت بالا و مناسب بودن برای عناصر هدفمند است.
from playwright.async_api import async_playwright
async def scroll_to_element_and_click():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto('https://books.toscrape.com')
# ورودی: یک selector برای دکمه "next"
next_handle = await page.query_selector("text='next'")
if next_handle:
# اسکرول میکنیم تا عنصر دیده شود
await next_handle.scroll_into_view_if_needed()
# خروجی: کلیک روی دکمه (اگر قابل کلیک بود)
await page.wait_for_timeout(1000)
await next_handle.click()
await browser.close()
توضیح قدمبهقدم:
- ورودی: یک selector (مثلاً
text='next'). - با query_selector یا locator المان را مییابیم.
- با scroll_into_view_if_needed() تضمین میکنیم المان در viewport قرار دارد تا عملیات بعدی (کلیک، اسکرپ کردن متن یا گرفتن screenshot) موفق شود.
- نکتهٔ امنیت و پایداری: قبل از کلیک همیشه وجود و قابلیت دیدهشدن المان را بررسی کنید تا خطاهای زمان اجرا کاهش یابد.
روش ۲: اسکرول به اندازهٔ مشخص
ایده: با window.scrollBy به اندازههای کوچک اسکرول کنیم و پس از هر گام بررسیکنیم آیا المان هدف قابل مشاهده شده یا خیر؛ این روش شبیه رفتار انسانی است و میتواند برای دور زدن برخی تشخیصهای بات مؤثر باشد.
# اسکرول مرحلهای تا پیدا شدن دکمه "next"
from playwright.async_api import async_playwright
async def step_scroll_until_visible():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto('https://books.toscrape.com')
max_iterations = 40
for _ in range(max_iterations):
# اسکرول به اندازهٔ 250 پیکسل
await page.evaluate('window.scrollBy(0, 250)')
await page.wait_for_timeout(500) # کمی معطل میکنیم تا محتوا بارگذاری شود
next_visible = await page.locator("text='next'").is_visible()
if next_visible:
await page.click("text='next'")
break
await browser.close()
نکات:
- مزیت: کنترل دقیقتر و امکان اضافهکردن تأخیرهای طبیعی بین اسکرولها (human-like).
- عیب: کندتر است و اگر سایت بهصورت پیوسته محتوا بارگذاری کند، نیاز به مدیریت بارگذاری دارید.
روش ۳: اسکرول تا انتهای صفحه
ایده: با یک اسکرولِ بزرگ (معمولاً document.body.scrollHeight) بلافاصله به انتهای صفحه برویم. سریع اما پرخطر از نظر شناسایی بهعنوان بات.
async def scroll_to_bottom_and_click():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto('https://books.toscrape.com')
# اسکرول به پایین صفحه براساس ارتفاع سند
await page.evaluate('window.scrollBy(0, document.body.scrollHeight)')
await page.wait_for_timeout(500)
if await page.locator("text='next'").is_visible():
await page.click("text='next'")
await browser.close()
تذکر: این روش برای سایتهایی که از محافظت قوی استفاده نمیکنند مناسبتر است؛ در سایتهای حساس این کار میتواند رفتار رباتی را برجسته کند.
روش ۴: اسکرول با میانبرهای صفحهکلید
ایده: از API صفحهکلید Playwright استفاده کنید (مثلاً Space یا PageDown). این روش ساده اما غیرقابلپیشبینی است، چرا که میزان اسکرول در هر بار فشردن ممکن است متفاوت باشد.
async def keyboard_scroll_example():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto('https://books.toscrape.com')
for _ in range(6):
await page.keyboard.press('Space')
await page.wait_for_timeout(800)
await browser.close()
وقتی دقت مورد نیاز است، توصیه میشود از روشهای مبتنی بر جاوااسکریپت (scrollBy یا scrollIntoView) استفاده کنید.
روش ۵: اسکرول با ماوس (Wheel)
ایده: شبیهسازی چرخ ماوس با page.mouse.wheel و حرکت نشانگر با page.mouse.move. برای شبیهسازی رفتار کاربر مفید است اما دقیق نیست.
async def mouse_wheel_scroll():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto('https://books.toscrape.com')
# حرکت چرخ ماوس؛ Y مثبت یعنی اسکرول پایین
await page.mouse.wheel(0, 200)
await page.mouse.move(100, 200)
await page.wait_for_timeout(500)
await browser.close()
نکته: این روش ممکن است در مرورگرها یا درایورهای مختلف مقادیر متفاوتی اسکرول تولید کند؛ تست کراسبراؤزر ضروری است.
روش ۶: شبیهسازی لمس (Touchscreen)
اگر هدف شما شبیهسازی دستگاه موبایل است، ساختن کانتکست با has_touch=True و استفاده از page.tap یا اسکرول با window.scrollBy ترکیب مناسبی است:
async def touchscreen_scroll():
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context(has_touch=True)
page = await context.new_page()
await page.goto('https://books.toscrape.com')
# اسکرول کوچک شبیه سوایپ
await page.evaluate('window.scrollBy(0, 30)')
await page.wait_for_timeout(300)
# لمس/تپ روی دکمه
if await page.locator("text='next'").is_visible():
await page.tap("text='next'")
await browser.close()
نکتهٔ عملی: بعضی سایتها فقط وقتی رفتارهای لمسی واقعی ببینند بارگذاری ادامه پیدا میکند؛ تنظیم has_touch میتواند کمک کند.
پرداختن به Lazy Loading
وقتی دادهها هنگام اسکرول پویا بارگذاری میشوند، باید صبر و هماهنگی بین اسکرول و بارگذاری جدید برقرار کنید. دو رویکرد رایج:
۱) زمانهای ثابت (Hardcoded waits)
سادگی دارد اما حساس به سرعت شبکه و تغییرات سایت است.
# مثال: اسکرول بزرگ سپس waitForTimeout
await page.evaluate('window.scrollBy(0, 10000)')
await page.wait_for_timeout(1000) # صبر ثابت برای بارگذاری تصاویر جدید
استفاده توصیهشده: در ترکیب با چکهایی که وجود المانهای تازه را بررسی میکنند.
۲) انتظار برای شبکه (Network waits)
صبر کردن تا زمانی که شبکه به حالت idle برود با wait_for_load_state('networkidle')؛ اما دقت کنید که بعضی سایتها پس از اسکرول هم ممکن است همچنان درخواستهای طولانیمدت یا polling داشته باشند و حالت networkidle بهتنهایی کافی نباشد.
# مثال: صبر تا networkidle
await page.wait_for_load_state('networkidle', timeout=10000)
بهترین عمل: ترکیب هر دو روش—ابتدا انتظار شبکه، سپس یک تأخیر کوتاه و در نهایت بررسی وجود محتوای جدید (مثلاً تعداد ایتمها تغییر کرده یا یک selector جدید ظاهر شده).
چالشها و نکات عملی
- بارگذاری پویا: شبکه idle تضمینکنندهٔ بارگذاری کامل نیست؛ از assertهایی مانند افزایش تعداد آیتمها یا وجود selector جدید استفاده کنید.
- زمانبندی و retry: برای عملیات حساس از الگوریتمهای retry با backoff استفاده کنید تا شکستهای موقتی را تحمل کنید.
- عملکرد و حافظه: اسکرول طولانی میتواند مصرف حافظهٔ مرورگر را بالا ببرد—از contextهای جدید، بستن تبها و مسدودسازی منابع غیرضروری (مثل تصاویر در صورت نیاز) استفاده کنید.
- شبیهسازی انسانی: اگر لازم است شبیه انسان باشید، از waitهای متغیر، حرکت موس و اسکرول مرحلهای استفاده کنید. اما زیاد پیچیده کردن رفتار میتواند نگهداری کد را دشوار کند.
- اسکرینشات طولانی: برای صفحات خیلی بلند، اسکرینشات مرحلهای بگیرید (بخشبخش) بهجای تلاش برای یک تصویر کامل.
- پاپآپها: pop-upها را در ابتدای جریان مدیریت کنید (چک، بستن یا تعامل) تا از خطاهای بعدی جلوگیری شود.
- سازگاری مرورگر: مقادیر اسکرول با هر مرورگر فرق میکند؛ تست روی Chromium/Firefox/WebKit ضروری است.
- امنیت و اخلاق: همیشه قوانین سایت (robots.txt) و قوانین محلی را رعایت کنید، و از ارسال درخواست با نرخ بیش از حد جلوگیری کنید.
نمونهٔ الگوی مقاوم (Retry + Backoff)
import asyncio
from playwright.async_api import async_playwright
async def click_with_retry(page, selector, retries=3):
delay = 0.5
for i in range(retries):
try:
handle = page.locator(selector)
await handle.wait_for(state='visible', timeout=5000)
await handle.click()
return True
except Exception:
await asyncio.sleep(delay)
delay *= 2
return False
این الگو ورودی: صفحه و selector؛ خروجی: True/False بسته به موفقیت کلیک. مزیت: مقاوم در برابر تاخیرهای موقتی و race conditionها.
نتیجهگیری
اسکرول کردن با Playwright چندین روش دارد و انتخاب درست بستگی به هدف شما، حساسیت سایت به رفتار ربات و نیاز به شبیهسازی کاربر دارد. برای پروژههای وب اسکریپینگ حرفهای:
- از ترکیب روشها و ترکیب wait_for_load_state و تأخیرهای هدفمند استفاده کنید.
- الگوهای retry و نظارت بر حافظه/پرفورمنس را پیادهسازی کنید.
- همیشه تعاملات را طوری طراحی کنید که ابتدا المانها قابل مشاهده و قابل تعامل باشند.
با درک تفاوت بین این روشها و اعمال بهترینروشهای ذکرشده، میتوانید اسکریپ پایدارتر و قابلاعتمادتر برای اسکریپ کردن صفحات طولانی و سایتهای با لیزیلودینگ ایجاد کنید.





