مقدمه
یکی از چالشهای اصلی در اسکریپینگِ مبتنی بر مرورگر، همگامسازی زمان اجرای اسکریپت با زمانهای بارگذاری متغیر صفحات و المانهاست. در این مقاله به صورت عملی انواع استراتژیهای انتظار در Selenium را بررسی میکنیم و یاد میگیرید چگونه با استفاده از روشهای مناسب: سرعت، پایداری و احتمال کشف شدن اسکریپرتان را بهینه کنید. پس از مطالعه این راهنما میتوانید:
- معمولترین روشهای انتظار را توضیح دهید و برای هر سناریو مناسبترین را انتخاب کنید.
- کد کاربردی پایتون با مثالهای کامل بنویسید و تفاوتها را بفهمید.
- نکات امنیتی، مدیریت خطا و بهبود عملکرد را در پیادهسازیهای واقعی اعمال کنید.
نصب Selenium و WebDriver
برای شروع نیاز به نصب خودِ بسته selenium و WebDriver مخصوص مرورگر (مثلاً ChromeDriver) دارید. نسخهٔ Chrome و نسخهٔ درایور باید همخوانی داشته باشند.
نصب Selenium با pip:
pip install seleniumنکتهها:
- بررسی نسخهٔ مرورگر در لینوکس: google-chrome --version یا معادل آن.
- برای محیطهای سرور معمولاً از Chrome یا Chromium در حالت headless استفاده میکنند؛ اما دقت کنید که بعضی سایتها رفتار headless را تشخیص میدهند.
چرا مدیریت زمان بارگذاری مهم است؟
دلایل عملی شامل موارد زیر است:
- تعامل با فرمها: بعد از ارسال فرم صفحه ممکن است تغییر کند یا عناصر جدید اضافه شوند.
- پاپآپها و مودالها: باید دقیقاً بدانید چه زمانی ظاهر میشوند تا بتوانید آنها را ببندید یا با آنها تعامل کنید.
- افزایش کارایی: انتظار مناسب از بارگذاری بیهودهٔ منابع جلوگیری میکند.
- کاهش احتمال شناسایی: رفتار طبیعیتر (مثلاً صبر برای نمایش محتوا) احتمال کشف شدن را کاهش میدهد.
روشهای رایج انتظار در Selenium
مجموعهٔ روشها شامل: انتظار ضمنی، انتظار صریح، Fluent Wait، مصرف time.sleep()، تنظیم تایماوت صفحه، شرایط انتظار سفارشی و انتظار برای JavaScript/Ajax است. در ادامه هر کدام با مثال و توضیحات خطبهخط بررسی میشوند.
انتظار ضمنی (Implicit Wait)
ایمپلیت ویت یک تایماوت پیشفرض برای کل WebDriver تنظیم میکند تا هنگام find_element یا find_elements تا آن مدت صبر کند. مزیت: ساده و تنظیم مرکزی. معایب: مناسب برای شرایط پیچیده یا عناصر با زمان بارگذاری متفاوت نیست و ممکن است اشکالزدایی را سخت کند.
from selenium import webdriver
from selenium.webdriver.common.by import By
url = "https://www.example.com"
driver = webdriver.Chrome()
# تنظیم انتظار ضمنی به 10 ثانیه
driver.implicitly_wait(10)
driver.get(url)
# تلاش برای پیدا کردن المنت با CSS selector
login_modal = driver.find_element(By.CSS_SELECTOR, ".nav-signin-tooltip-footer")
print(login_modal.text)
driver.quit()توضیح ورودیها/خروجیها و نقشها:
- ورودی: url (رشته)، CSS selector (رشته)
- خروجی: عنصرِ پیدا شده یا پرتاب NoSuchElementException پس از تایماوت
- نقش تابع: driver.implicitly_wait تعیین میکند تا چه مدت توابع بازیابی عنصر به دنبال آن بگردند.
خطبهخط:
- ایجاد WebDriver
- تنظیم تایماوت ضمنی
- رفتن به url
- فراخوانی find_element که تا 10 ثانیه منتظر میماند
انتظار صریح (Explicit Wait)
انتظار صریح با WebDriverWait و شرایط پیشبینیشده (Expected Conditions) به شما اجازه میدهد روی یک عنصر یا شرط مشخص منتظر بمانید؛ مثلاً تا وقتی عنصر قابل کلیک باشد یا متن خاصی در آن ظاهر شود. این روش برای همگامسازی دقیقتر مناسب است.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
url = "https://www.example.com"
driver = webdriver.Chrome()
driver.get(url)
try:
# تا 10 ثانیه منتظر پیدا شدن المنت باش
login_modal = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".nav-signin-tooltip-footer"))
)
print(login_modal.text)
finally:
driver.quit()توضیح:
- ورودی: WebDriver، تایماوت، شرط EC
- خروجی: عنصر یا پرتاب TimeoutException
- مزیت: کنترل دقیق روی شرط (قابل مشاهده بودن، کلیکپذیری، متن و غیره)
نکتهٔ عملی: هنگام استفاده از explicit wait معمولاً بهتر است از شرطهایی مانند visibility_of_element_located یا element_to_be_clickable استفاده کنید تا مطمئن شوید المان واقعاً آمادهٔ تعامل است.
Fluent Wait
Fluent Wait همان انتظاری است که دورهٔ poll (فاصلهٔ بررسی) و نادیدهگرفتن استثناها را کنترل میکند. در Python کلاس جداگانهای وجود ندارد اما میتوان از پارامترهای WebDriverWait مانند poll_frequency و مدیریت استثناها استفاده کرد.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
url = "https://www.example.com"
driver = webdriver.Chrome()
driver.get(url)
# poll_frequency=1 یعنی هر 1 ثانیه DOM را بررسی کن
wait = WebDriverWait(driver, timeout=10, poll_frequency=1)
login_modal = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".nav-signin-tooltip-footer")))
print(login_modal.text)
driver.quit()نکات:
- با کاهش poll_frequency تعداد بررسیها بیشتر و تأثیر روی کارایی بیشتر میشود؛ انتخاب مناسب وابسته به حساسیت صفحه است.
- میتوانید استثناهایی مثل NoSuchElementException را هنگام wait نادیده بگیرید.
استفاده از time.sleep()
time.sleep() اجرای اسکریپت را بهصورت بلاککننده متوقف میکند. این روش ساده است اما در تولید توصیه نمیشود مگر برای شرایط محدود (مانند بازرسی دستی، مشاهده، یا حل کپچا توسط انسان).
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep
driver = webdriver.Chrome()
driver.get("https://www.example.com")
# توقف صریح به مدت 3 ثانیه
sleep(3)
login_modal = driver.find_element(By.CSS_SELECTOR, ".nav-signin-tooltip-footer")
print(login_modal.text)
driver.quit()معایب:
- هدررفتن زمان اگر صفحه زودتر بارگذاری شود.
- ممکن است هنوز صفحه آماده نباشد و خطا رخ دهد اگر sleep کوتاهتر از نیاز واقعی باشد.
تنظیم Page Load Timeout
با set_page_load_timeout میتوانید محدودیت زمانی برای کل بارگذاری صفحه تعیین کنید. اگر صفحه در زمان مشخص بارگذاری نشود، Selenium استثنا پرتاب میکند و شما میتوانید آن را مدیریت کنید تا ادامهٔ کراول انجام شود.
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
# تعیین حداکثر زمان 10 ثانیه برای بارگذاری کامل صفحه
driver.set_page_load_timeout(10)
try:
driver.get("https://www.example.com")
except Exception as e:
print("Page load timeout:", e)
# سپس میتوانیم از روشهای دیگری برای بارگذاری یا نادیده گرفتن ادامه استفاده کنیم
driver.quit()کاربرد: در کراولهای بزرگ که لیست طولانی صفحات دارید و نمیخواهید روی صفحات کند گیر کنید.
انتظار برای JavaScript / Ajax (set_script_timeout)
برای صحنههایی که محتوای صفحه توسط JavaScript یا درخواستهای Ajax بارگذاری میشود، تنظیم set_script_timeout و اجرای جاوااسکریپت میتواند مفید باشد. این تابع زمان اجرای اسکریپتهای سمت کلاینت را محدود میکند.
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
# زمان مجاز برای اجرای اسکریپتها
driver.set_script_timeout(10)
driver.get("https://www.example.com")
# میتوانیم با اجرای JS وضعیت لود را بررسی کنیم
is_ready = driver.execute_script("return document.readyState;")
print("readyState:", is_ready)
# یا منتظر شویم که یک مقدار JS خاص پیدا شود
# سپس ادامه عملیات
driver.quit()نکته: برای اپلیکیشنهای تک صفحهای (SPA) معمولاً نیاز به بررسی متغیرهای JS یا فراخوانیهایی مانند چک وضعیت یک فانکشن دلخواه دارید.
شرایط انتظار سفارشی (Custom Wait Conditions)
گاهی منطق انتظار پیچیده میشود؛ مثلاً باید چند شرط ترکیبی بررسی شود. بهترین کار نوشتن یک تابع انتظار سفارشی است که توسط WebDriverWait فراخوانی میشود و یک المان یا True/False برمیگرداند.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# تابع شرط سفارشی: بازگرداندن عنصر در صورت وجود
def wait_for_selector(driver, selector, timeout=10):
wait = WebDriverWait(driver, timeout)
return wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, selector)))
driver = webdriver.Chrome()
selector = ".nav-signin-tooltip-footer"
driver.get("https://www.example.com")
login_modal = wait_for_selector(driver, selector)
print(login_modal.text)
driver.quit()توضیح: این الگو تمیز است، قابل تست و قابل استفاده مجدد در پروژهها؛ همچنین امکان افزودن لاگینگ، retry و metrics را بهراحتی دارد.
مثالهای عملی: صفحه، عنصر، پذیرش کوکی
سه سناریوی متداول:
- منتظر ماندن برای بارگذاری کلی صفحه
- منتظر ماندن برای یک عنصر مشخص (مثلاً مودال یا دکمه)
- کلیک کردن روی دکمهٔ پذیرش کوکی
مثال: پذیرش کوکی و اسکرپینگ داده:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
url = "https://www.example.com"
driver = webdriver.Chrome()
driver.get(url)
wait = WebDriverWait(driver, 20)
# فرضاً selector دکمهٔ بستن/پذیرش کوکی
cookie_selector = ".onetrust-close-btn-ui"
try:
cookie_btn = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, cookie_selector)))
cookie_btn.click()
except Exception:
print("Cookie button not found or clickable; proceeding")
# سپس ادامه اسکرپینگ عناصر مورد نظر
# مثال: استخراج یک عنوان
try:
title_el = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "h1")))
print(title_el.text)
finally:
driver.quit()نکته فنی: همیشه برای کلیک کردن روی المانها از element_to_be_clickable استفاده کنید تا از کلیک روی المان نامرئی جلوگیری شود.
مدیریت خطاها، Retry و پایداری
در عملیات واقعی باید موارد زیر را در نظر بگیرید:
- استفاده از بلوکهای try/except برای گرفتن TimeoutException یا NoSuchElementException و تصمیمگیری برای retry یا ثبت لاگ.
- پیادهسازی استراتژی retry با backoff نمایی برای صفحات ناپایدار.
- کنترل استثناها و بستن مناسب WebDriver در finally تا منابع آزاد شوند.
عملکرد، مصرف منابع و نکات امنیتی
عملکرد:
- کاهش poll_frequency باعث افزایش استفاده از CPU میشود؛ تنظیم مناسب بین responsiveness و بار پردازشی مهم است.
- در کراولهای حجیم از headless mode و pooling مرورگرها یا تقسیم کار بین چند فرایند/کانتینر استفاده کنید.
امنیت و جلوگیری از شناسایی:
- تا حد امکان از رفتار انسانی تقلید کنید: زمانهای تصادفی بین کلیکها، اسکرول تدریجی و استفادهٔ معقول از waitها.
- نکات مربوط به footprint: تنظیم user-agent، مدیریت کوکیها و استفاده از پراکسی در صورت نیاز.
بهترینروشها (Best Practices)
- همیشه از انتظار صریح برای المانهای حساس استفاده کنید؛ انتظار ضمنی را فقط بهعنوان پیشفرض در نظر بگیرید.
- برای عملیات ترکیبی (چند شرط) از شروط سفارشی استفاده کنید تا کد خواناتر و قابل نگهداری شود.
- از time.sleep تنها در مواقع نیاز واقعی یا برای اشکالزدایی استفاده کنید؛ در تولید این روش را کم کنید.
- تایماوتها را معقول تعیین کنید (نه خیلی کوتاه که منجر به false-negative شود و نه خیلی بلند که زمان اجرا را طولانی کند).
- لاگبرداری از وقایع انتظار و استثناها را فعال کنید تا بتوانید الگوهای شکست را تشخیص دهید.
جمعبندی
مدیریت زمان بارگذاری در اسکریپینگ با Selenium ترکیبی از انتخاب درست ابزار و نوشتن کد مقاوم است. انتظار صریح و شرایط سفارشی معمولاً بهترین سطح کنترل را فراهم میکنند، Fluent Wait برای سایتهای با رفتار متغیر مفید است و time.sleep() باید به ندرت و کنترلشده به کار رود. در پروژههای واقعی، مدیریت خطا، لاگینگ، retry و شبیهسازی رفتار انسانی به پایداری و قابلیت تولید کمک زیادی میکنند.
با پیادهسازی الگوهای توضیحدادهشده میتوانید اسکریپتهای قابل اطمینانتر، سریعتر و با ریسک کمتر برای شناسایی بسازید.
آسان اسکریپ را در گوگل بهعنوان منبع ترجیحی انتخاب کن
مقالههای جدید ما زودتر و پررنگتر در نتایج گوگل و Discover برایت نمایش داده میشود. افزودن از تنظیمات گوگل





