مقدمه
در این راهنمای عملی برای توسعهدهندگان پایتون با سطح متوسط، به مقایسهٔ دو رویکرد متداول در وب اسکریپینگ میپردازیم: Selenium و Pyppeteer. هدف این مقاله این است که تفاوتهای کلیدی، سناریوهای مناسب، نکات پیادهسازی، و مثالهای کد واقعی را به شما نشان دهد تا در پایان بتوانید تصمیم بگیرید کدام ابزار برای پروژهٔ شما مناسبتر است.
پس از خواندن این مطلب شما خواهید دانست چگونه هر کدام را نصب کنید، نمونههای عملی برای باز کردن صفحات و استخراج اطلاعات بنویسید، چگونه پروکسی و سرشماری همزمانی را مدیریت کنید و بهینهسازی، مدیریت خطا و نکات ضد-تشخیص را پیاده کنید.
Pyppeteer چیست؟
Pyppeteer یک پورتی از کتابخانهٔ Puppeteer (نود) برای پایتون است که از پروتکل DevTools برای کنترل Chrome/Chromium استفاده میکند. این ابزار بهویژه برای صفحات جاوااسکریپتمحور مناسب است و قابلیتهای قدرتمندی مثل اجرای اسکریپت در مرورگر، رهگیری درخواستها و حالت headless را دارد.
مزایا و معایب Pyppeteer
- مزایا:
- پشتیبانی ذاتی از async/await برای اجرای همزمان صفحات و بهبود سرعت.
- دسترسی مستقیم به Chrome DevTools Protocol برای امور پیشرفته مثل رهگیری شبکه و گرفتن هار (HAR).
- عملکرد مناسب در حالت headless و رندر کامل صفحات جاوااسکریپتی.
- قابلیت اعمال هدرها و رهگیری/عبور درخواستها (request interception).
- معایب:
- محدود به Chrome/Chromium است و پشتیبانی چندمرورگری ندارد.
- پروژهٔ Pyppeteer رسماً در بعضی نسخهها کمتر نگهداری شده؛ بنابراین باید از ریسکهای سازگاری با نسخههای جدید مرورگر آگاه باشید.
- ممکن است مصرف منابع بالایی داشته باشد و تشخیص توسط مکانیزمهای ضد-اسکریپینگ آسانتر باشد مگر اینکه تدابیر پنهانسازی (stealth) اتخاذ شود.
نصب و راهاندازی Pyppeteer
پیشنیاز: پایتون 3.6 یا بالاتر و pip. نصب ساده است:
pip install pyppeteerاولین اجرای Pyppeteer معمولاً یک نسخهٔ سازگار از Chromium را دانلود میکند. اگر میخواهید دستی دانلود کنید، میتوانید از تابع دانلود کرومیوم در پکیج استفاده کنید (نمونه پایین در بخشهای پیشرفتهتر نشان داده میشود).
نمونهٔ پایه Pyppeteer
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=True)
page = await browser.newPage()
await page.goto('https://quotes.toscrape.com/')
title = await page.title()
print('Page title:', title)
await browser.close()
asyncio.run(main())شرح: این اسکریپت یک مرورگر Chromium را (در حالت headless) راهاندازی میکند، یک تب جدید باز میکند، به آدرس وارد شده میرود، عنوان صفحه را میخواند و سپس مرورگر را میبندد.
ورودی: هیچ ورودی خارجی لازم نیست بهجز آدرس داخل page.goto. خروجی: چاپ عنوان صفحه و بسته شدن مرورگر. نکات خط به خط:
- ایجاد روال main به صورت async تا بتوان از await استفاده کرد.
- launch مرورگر را شروع میکند؛ پارامترها مثل headless قابل تنظیم هستند.
- newPage یک تب جدید میسازد و goto صفحهٔ مورد نظر را باز میکند.
نمونهٔ پیشرفته Pyppeteer: رهگیری درخواست و همزمانی
import asyncio
import json
import tempfile
import shutil
from pyppeteer import launch
import pyppeteer.errors
async def handle_request(req):
# حذف تصاویر و ست کردن هدرهای سفارشی
headers = {**req.headers, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
try:
if req.resourceType == 'image':
await req.abort()
else:
await req.continue_({'headers': headers})
except Exception as e:
print('Request handling error:', e)
async def extract_product_details(browser, url, semaphore):
async with semaphore:
page = None
try:
page = await browser.newPage()
await page.setRequestInterception(True)
page.on('request', lambda req: asyncio.create_task(handle_request(req)))
await page.goto(url, waitUntil='domcontentloaded', timeout=120000)
await page.waitForSelector('span#productTitle', timeout=60000)
name = await page.evaluate('() => document.querySelector("span#productTitle").innerText')
price = await page.evaluate('() => (document.querySelector(".a-price-whole") || {}).innerText')
return {'name': name.strip() if name else 'not available', 'price': price or 'not available'}
except pyppeteer.errors.TimeoutError:
return {'name': 'not available', 'price': 'not available'}
except Exception as e:
print('Error in extract:', e)
return {'name': 'not available', 'price': 'not available'}
finally:
if page and not page.isClosed():
await page.close()
async def scrape_product_list(urls):
temp_dir = tempfile.mkdtemp()
try:
browser = await launch(headless=True, args=['--no-sandbox'], userDataDir=temp_dir)
semaphore = asyncio.Semaphore(3) # محدودیت همزمانی
tasks = [extract_product_details(browser, u, semaphore) for u in urls]
results = await asyncio.gather(*tasks)
with open('output_pyppeteer.json', 'w') as f:
json.dump(results, f)
finally:
await browser.close()
shutil.rmtree(temp_dir, ignore_errors=True)
# اجرا
# asyncio.run(scrape_product_list(['https://example.com/product1', 'https://example.com/product2']))شرح کلی: این نسخه نشان میدهد چگونه با رهگیری درخواستها (برای حذف تصاویر و تنظیم هدر) و محدود کردن همزمانی با Semaphore، سرعت و پایداری را بهبود دهیم.
نکات کلیدی:
- handle_request هدرها را اصلاح و منابع سنگین را مسدود میکند تا مصرف پهنای باند کاهش یابد.
- Semaphore برای جلوگیری از باز کردن بیش از حد تبها که میتواند منجر به بلاک یا مصرف زیاد شود استفاده شده.
- پاکسازی پوشهٔ کاربری موقت (userDataDir) برای جلوگیری از نشتی فایل روی دیسک ضروری است.
Selenium چیست؟
Selenium یک فریمورک شناختهشده برای اتوماسیون مرورگرهاست که از WebDriver برای کنترل مرورگرهای مختلف مثل Chrome، Firefox، Edge و Safari استفاده میکند. Selenium برای تست و همچنین اسکریپینگ پیچیدهی چندمرورگری مناسب است.
مزایا و معایب Selenium
- مزایا:
- پشتیبانی گسترده از مرورگرها و نسخههای قدیمیتر.
- اکوسیستم بالغ، پلاگینها و جامعهٔ بزرگ.
- قابلیت اجرای موازی از طریق Selenium Grid یا راهحلهای مشابه.
- انتظارات صریح (Explicit Waits) برای اطمینان از بارگذاری عناصر.
- معایب:
- معماری اصلی همزمان (synchronous) است؛ برای همزمانی باید از پروسههای مجزا یا Grid استفاده کنید.
- راهاندازی شامل نصب WebDriver برای هر مرورگر است و ممکن است پیچیدهتر باشد.
- ممکن است در بعضی کارها نسبت به ابزارهای async کندتر باشد.
نصب و نمونهٔ پایه Selenium
نصب کتابخانهٔ پایتون:
pip install seleniumنمونهٔ سادهٔ باز کردن صفحه:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
driver = webdriver.Chrome(options=options)
try:
driver.get('http://www.example.com')
print('Title:', driver.title)
finally:
driver.quit()شرح: در Selenium بهطور معمول یک WebDriver (مثل ChromeDriver) لازم است. driver.get صفحه را بارگذاری میکند و driver.quit مرورگر را میبندد.
Selenium با پروکسی (نمونهٔ خلاصه)
# مدل ساده شده برای استفاده از selenium-wire جهت اضافه کردن پروکسی
from seleniumwire import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--headless')
proxy_options = {
'proxy': {
'http': 'http://API_KEY@proxy.scrapeops.io:5353',
'https': 'http://API_KEY@proxy.scrapeops.io:5353',
}
}
driver = webdriver.Chrome(options=options, seleniumwire_options=proxy_options)
# سپس از driver برای ناوبری و استخراج استفاده کنید
توضیح: selenium-wire اجازهٔ رهگیری و تنظیم پروکسی را میدهد. هر نمونهٔ WebDriver باید با گزینههای پروکسی راهاندازی شود، و در محیطهای موازی باید برای هر instance تنظیمات جداگانه لحاظ گردد.
نمونهٔ استخراج محصول با Selenium (ساختار پیمایشی)
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 extract_product_details(driver, product_url, original_window):
# باز کردن تب جدید و استخراج عنوان و قیمت
driver.execute_script("window.open('');")
new_window = [w for w in driver.window_handles if w != original_window][0]
driver.switch_to.window(new_window)
driver.get(product_url)
try:
WebDriverWait(driver, 30).until(EC.presence_of_element_located((By.ID, 'productTitle')))
except Exception:
pass
try:
name = driver.find_element(By.ID, 'productTitle').text
except Exception:
name = 'not available'
try:
price = driver.find_element(By.CSS_SELECTOR, 'span.a-price-whole').text
except Exception:
price = 'not available'
driver.close()
driver.switch_to.window(original_window)
return {'name': name, 'price': price}
شرح: این تابع یک تب جدید باز میکند، صفحهٔ محصول را بارگذاری میکند، با WebDriverWait منتظر المان میماند، متنها را استخراج میکند و سپس تب را میبندد و به تب اصلی بازمیگردد. ورودیها: driver (WebDriver)، product_url و original_window. خروجی: یک دیکت با نام و قیمت یا مقدار «not available».
مقایسهٔ دقیق ویژگیها
- پشتیبانی مرورگر: Selenium چندمرورگری است؛ Pyppeteer محدود به Chromium.
- همزمانی: Pyppeteer با async/await داخلی راحتتر همزمانی را مدیریت میکند؛ Selenium معمولا نیاز به فرایند/فریمورک جداگانه دارد.
- دسترسی به DevTools: Pyppeteer دسترسی مستقیم دارد؛ Selenium از طریق WebDriver لایهای بالاتر عمل میکند.
- نصب و نگهداری: Pyppeteer نصب سادهتری دارد ولی ممکن است کمتر نگهداری شود؛ Selenium نصب پیچیدهتر ولی اکوسیستم بزرگتری دارد.
- یکپارچگی با تست: Selenium گزینهٔ قابل قبولی برای تست و CI است؛ Pyppeteer مناسب استخراج سریع و رندرهای پیچیدهٔ جاوااسکریپتی.
مطالعهٔ موردی: اسکریپینگ آمازون (نکات عملی)
هر دو نمونه میتوانند اطلاعات محصول را استخراج کنند، اما برای جلوگیری از مسدود شدن هنگام اسکریپینگ آمازون باید موارد زیر را رعایت کنید:
- استفاده از پروکسیهای چرخان و مدیریت IP برای توزیع درخواستها.
- چرخاندن User-Agent و اعمال هدرهای واقعی مرورگر.
- محدود کردن نرخ درخواستها و استفاده از وقفهٔ تصادفی (randomized delays).
- مسدودسازی منابع غیرضروری (تصاویر، فونتها) تا مصرف پهنای باند کاهش یابد.
- استفاده از تبها یا صفحات موقت و پاکسازی userDataDir برای جلوگیری از تجمع فایلهای موقتی.
- برای تعامل با محتوای جاوااسکریپتی پیچیده، Pyppeteer اغلب پیادهسازی سادهتری ارائه میدهد؛ اما برای اجرای همزمان و مقیاس بزرگ، پیادهسازی Selenium با Grid یا orchestration بیرونی بهتر است.
نکات امنیتی، حقوقی و بهترین روشها
- قبل از اسکریپینگ، سیاستهای سایت و قوانین محلی را بررسی کنید؛ بعضی سایتها اسکریپینگ را منع میکنند.
- از احراز هویت و توکنها بهدرستی محافظت کنید (مقدار API Key را در کد سختکد نکنید).
- از استراتژیهای retry با backoff تصاعدی استفاده کنید تا فشار روی سرور کاهش یابد.
- برای جلوگیری از نشتی منابع، همواره در بلوکهای finally یا try/finally صفحات و مرورگرها را ببندید و دایرکتوریهای موقت را پاک کنید.
عملکرد، پایایی و مانیتورینگ
برای پروژههای تولیدی:
- لاگینگ و مانیتورینگ خطاها را پیاده کنید تا صفحات یا درخواستهای مشکلدار شناسایی شوند.
- از health checks و متریکهای مصرف حافظه/CPU برای هر instance اطلاع داشته باشید.
- در صورت نیاز به مقیاسپذیری، از صفها (مثل Redis/RabbitMQ) و workerهای مستقل استفاده کنید تا مدیریت توزیع کار آسانتر شود.
جمعبندی و توصیهٔ نهایی
خلاصهٔ سریع:
- اگر نیاز به پشتیبانی چندمرورگری، یکپارچگی با تست و اکوسیستم بالغ دارید، Selenium انتخاب مطمئنی است.
- اگر کار شما شامل تعداد زیادی صفحات جاوااسکریپتی است و دنبال سادهسازی همزمانی و سرعت هستید، Pyppeteer انتخاب بهتری خواهد بود.
در عمل، برای بسیاری از پروژهها ترکیب ابزارها میتواند مفید باشد: از Pyppeteer برای صفحات جاوااسکریپتمحور و از Selenium برای سناریوهای نیازمند چندمرورگری یا تست استفاده کنید. فراموش نکنید که پروکسی، هدرها، نرخمحدودیت و مدیریت خطاها اغلب مهمترین عوامل موفقیت در تولید اسکریپرهای پایدار هستند.





