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

اسکریپینگ با Selenium: پیمایش صفحات وب

اسکریپینگ با Selenium: پیمایش صفحات وب
این راهنمای فارسی، تکنیک‌های پیمایش صفحه با Selenium را برای اسکریپینگ پوشش می‌دهد؛ از اجرای جاوااسکریپت و اسکرول به المان تا ActionChains و مدیریت infinite-scroll. شامل مثال‌های پایتون، توضیحات خط‌به‌خط، نکات هماهنگ‌سازی و راه‌حل‌های عملی برای مشکلات رایج است.
آسان اسکریپ آسان اسکریپ
1405-06-10

مقدمه

پیمایش (اسکرول) صفحات در فرایند وب اسکریپینگ نقش اساسی دارد؛ مخصوصاً وقتی صفحات به صورت lazy-loaded یا infinite-scroll محتوا را به‌صورت پویا بارگذاری می‌کنند. در این مقاله قدم‌به‌قدم روش‌های مختلف پیمایش با Selenium را توضیح می‌دهم، مثال‌های پایتون همراه با توضیحات خط‌به‌خط، نکات مربوط به هماهنگ‌سازی، عملکرد و امنیت و راه‌حل‌های عملی برای چالش‌های رایج ارائه می‌شود. پس از خواندن این راهنما شما خواهید توانست صفحات دینامیک را به‌درستی پیمایش، محتوای جدید را شناسایی و داده‌ها را با پایداری استخراج کنید.

مرور سریع روش‌ها

  • اجرای جاوااسکریپت با driver.execute_script (مثال: window.scrollTo / window.scrollBy).
  • اسکرول به المان با scrollIntoView().
  • ارسال کلیدها (مثل Keys.PAGE_DOWN, Keys.END).
  • اسکرول به مختصات با window.scrollTo(x, y).
  • اسکرول به اندازه مشخص با window.scrollBy.
  • ActionChains برای عملیات پیچیده یا افقی/مورب.

اجرای جاوااسکریپت با driver.execute_script

ایده کلی: اجرای مستقیم توابع جاوااسکریپت داخل مرورگر سریع و قابل‌اعتماد است و برای اسکرول‌های سراسری مناسب است.

ورودی: driver (WebDriver) و کد جاوااسکریپت؛ خروجی: معمولاً هیچ مقدار برنمی‌گرداند مگر اینکه از return استفاده کنید.

# نمونهٔ ساده: اسکرول تا پایین و سپس تا بالا
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")  # به پایین
# ... صبر/انتظار برای بارگذاری محتوا
driver.execute_script("window.scrollTo(0, 0)")  # بازگشت به بالا

شرح خط‌ها: اول ارتفاع کامل صفحه را می‌گیریم و به پایین می‌رویم؛ سپس برای مشاهده یا بارگذاری محتوا مکث می‌کنیم؛ در انتها باز می‌گردیم. نکته: اگر نیاز به مقدار برگشتی دارید از "return document.body.scrollHeight" استفاده کنید.

اسکرول به المان با scrollIntoView()

ایده: وقتی می‌دانیم المان موردنظر کجاست، با آوردن آن در viewport می‌توانیم تعاملاتی مثل کلیک یا خواندن متن انجام دهیم.

from selenium.webdriver.common.by import By

# پیدا کردن المان و اسکرول کردن تا آن
element = driver.find_element(By.XPATH, '//*[@id="target"]')
driver.execute_script("arguments[0].scrollIntoView({block: 'center'})", element)

ورودی: یک WebElement؛ خروجی: تغییر موقعیت viewport. توضیح جزئی: گزینهٔ block می‌تواند 'start', 'center' یا 'end' باشد تا جایگاه نهایی المان در صفحه مشخص شود (برای دقت بیشتر مفید است).

استفاده از کلیدهای صفحه (Keys)

ایده: شبیه‌سازی رفتار کاربر با ارسال کلیدها به عناصر قابل‌گیرایی مانند body. مفید برای تعامل با سایت‌هایی که براساس فشار کلید محتوا می‌آورند.

from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

body = driver.find_element(By.TAG_NAME, 'body')
body.send_keys(Keys.PAGE_DOWN)  # یک صفحه به پایین
# یا با END برای رفتن به انتها
body.send_keys(Keys.END)

نکتهٔ عملی: بعضی سایت‌ها به دلیل فشرده بودن JS یا شنیدن رویدادهای خاص، به رویدادهای کلیدی پاسخ متفاوت می‌دهند؛ بنابراین گاهی باید ترکیب کلیدها یا تاخیرها را تنظیم کنید.

اسکرول با مختصات و با مقدار مشخص

ایده: وقتی نیاز دارید دقیقاً به موقعیت X,Y بروید یا به اندازه مشخصی اسکرول کنید.

# اسکرول به مختصات مشخص
x, y = 0, 1000
driver.execute_script(f"window.scrollTo({x}, {y})")

# اسکرول به اندازهٔ 500 پیکسل نسبت به موقعیت فعلی
driver.execute_script("window.scrollBy(0, 500)")

کاربرد: جدول‌های عرضی یا اسکرول‌های افقی با تغییر پارامترهای x انجام می‌شود. مراقب باشید که صفحه‌های با layout متغیر ممکن است مقادیر متفاوتی نشان دهند.

ActionChains برای اسکرول‌های پیچیده

ایده: برای حرکت به نقاط خاص، اسکرول مورب یا ترکیب حرکت و کلیک از ActionChains استفاده کنید.

from selenium.webdriver.common.action_chains import ActionChains

action = ActionChains(driver)
element = driver.find_element(By.ID, 'some-id')
# حرکت به سمت المان و اجرای عمل
action.move_to_element(element).perform()

توضیح: move_to_element معمولاً باعث می‌شود مرورگر اسکرول کند تا المان در دید قرار بگیرد. برای تعاملات ترکیبی (مثلاً drag & drop) مفید است.

موارد کاربردی پیمایش

در عمل با سه سناریوی رایج مواجه می‌شوید:

  • عمودی: صفحات فید یا لیست‌ها. روش‌های پیشنهادی: window.scrollBy در حلقه همراه با WebDriverWait.
  • افقی: جداول یا گالری‌های عریض؛ از window.scrollBy(x, 0) یا ActionChains استفاده کنید.
  • افزایشی / Infinite: اسکرول در حلقه تا وقتی ارتفاع صفحه افزایش نیابد یا تعداد آیتم‌ها ثابت شود.

چالش‌های رایج و راه‌حل‌ها

  • Infinite scrolling:
    # نمونهٔ متداول: اسکرول تا انتهای محتوا
    import time
    last_height = driver.execute_script("return document.body.scrollHeight")
    while True:
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(2)  # زمان لازم برای بارگذاری
        new_height = driver.execute_script("return document.body.scrollHeight")
        if new_height == last_height:
            break
        last_height = new_height

    نکتهٔ عملی: مکث‌ها را با توجه به سرعت سرور/شبکه تنظیم کنید و از محدودکنندهٔ تعداد حلقه‌ها برای جلوگیری از حلقهٔ بی‌نهایت استفاده کنید.

  • Dealing with lazy loading:

    راه‌حل‌ها:

    1. اسکرول تدریجی در گام‌های کوچک و وقفهٔ مناسب.
    2. استفاده از WebDriverWait برای انتظار نمایش المان‌های جدید.
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.common.by import By
    
    # بعد از هر اسکرول، منتظر بمانید تا المانی ظاهر شود
    driver.execute_script("window.scrollBy(0, 500)")
    WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.item-loaded')))
  • دقت اسکرول (offset):

    گاهی اسکرول به‌دقت کافی نمی‌آورد؛ استفاده از تنظیمات scrollIntoView با گزینه‌های block/inline کمک می‌کند:

    driver.execute_script("arguments[0].scrollIntoView({block: 'center', inline: 'center'});", element)
  • مشکلات عملکرد:

    اسکرول‌های مکرر و بدون وقفه باعث بار روی CPU/شبکه می‌شوند. روش‌ها:

    • استفاده از تاخیر هوشمند و backoff.
    • افزایش اندازهٔ پنجره با driver.set_window_size(1920, 1080) تا آیتم‌های بیشتری بارگذاری شود.
  • Cross-browser:

    رفتار اسکرول ممکن است بین مرورگرها متفاوت باشد؛ بنابراین در تست‌های مهم، در چند مرورگر اجرا کنید و در صورت نیاز تنظیمات مخصوص مرورگرها را اعمال کنید (مثلاً گزینه‌های Firefox).

  • پاپ‌آپ‌ها و اورلی‌ها:

    قبل از اسکرول بررسی کنید که پوشش یا modal مانع نیست. از WebDriverWait یا منطق بسته‌شدن مودال استفاده کنید.

  • فریم‌ها و اسکرول داخلی:

    برای اسکرول داخل iframe یا div با overflow ابتدا به داخل فریم سوئیچ کنید:

    frame = driver.find_element(By.ID, 'frame_id')
    driver.switch_to.frame(frame)
    driver.execute_script("window.scrollTo(0, 200);")
    driver.switch_to.default_content()
  • Mobile / touch:

    برای شبیه‌سازی لمس از TouchActions یا ویژگی‌های دستگاه موبایل در Chrome/DevTools استفاده کنید؛ رفتار با دسکتاپ متفاوت است.

  • همگام‌سازی (Synchronization):

    همیشه از explicit waits به جای time.sleep استفاده کنید تا پایدارتر و سریع‌تر باشید.

مثال عملی: پیمایش تا انتها و جمع‌آوری تصاویر (Real-world)

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

import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def scroll_to_end_and_collect_images(driver, max_iters=30, pause=2):
    """
    ورودی:
      - driver: یک WebDriver فعال
      - max_iters: حداکثر دفعات اسکرول تا جلوگیری از حلقهٔ بی‌نهایت
      - pause: ثانیه‌ها برای صبر بین هر اسکرول
    خروجی:
      - لیستی از src تصاویر جمع‌آوری‌شده
    """
    last_height = driver.execute_script("return document.body.scrollHeight")
    iters = 0
    while iters < max_iters:
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(pause)
        # اگر نیاز داریم صبر کنیم تا یک عنصر خاص لود شود:
        try:
            WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.TAG_NAME, 'img')))
        except Exception:
            pass
        new_height = driver.execute_script("return document.body.scrollHeight")
        if new_height == last_height:
            break
        last_height = new_height
        iters += 1

    imgs = driver.find_elements(By.TAG_NAME, 'img')
    return [img.get_attribute('src') for img in imgs if img.get_attribute('src')]

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

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

  • به سایت‌ها فشار بیش از حد وارد نکنید: بین اسکرول‌ها تاخیر مناسب بگذارید و از نرخ درخواست بالا پرهیز کنید.
  • از explicit waits استفاده کنید تا وابستگی به time.sleep کاهش یابد.
  • خطاها را با try/except مدیریت و در صورت نیاز لاگ کنید تا اسکریپت شما قابل‌اعتماد شود.
  • در صورتی که به داده‌های حساس دسترسی دارید، محافظت از اطلاعات ذخیره‌شده و رعایت قوانین سایت را جدی بگیرید.
  • برای اجرای در مقیاس، از صف‌بندی کارها، مدیریت پروکسی و نرخ‌دهی (rate limiting) استفاده کنید.

جمع‌بندی

پیمایش صفحه با Selenium چندین روش دارد که هرکدام برای سناریوهای متفاوت مناسب‌اند: اجرای جاوااسکریپت برای اسکرول مستقیم، اسکرول به المان برای دقت بیشتر، ارسال کلیدها برای رفتار انسانی و ActionChains برای تعاملات پیچیده. مهم‌ترین نکات عملی عبارت‌اند از استفاده از waits به‌جای sleep، مدیریت حلقه‌ها برای infinite scroll، و تست در چند مرورگر. با ترکیب این تکنیک‌ها می‌توانید صفحات دینامیک را پایدار و مؤثر اسکریپ کنید و داده‌های موردنیاز را با خطر کمتر از دست دادن آیتم‌ها استخراج کنید.

اگر نیاز دارید می‌توانم یک اسکریپت آمادهٔ کاربردی بر اساس یک URL مشخص برایتان بنویسم یا نمونه‌ها را برای اجرای در headless و در محیط‌های ابری تنظیم کنم.

مطالب مرتبط

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