

در این راهنمای عملی برای توسعهدهندگان Python سطح متوسط، قدمبهقدم میسازیم یک سیستم وب اسکریپینگ با Scrapy که صفحات جستجو و محصول آمازون را پیدا و دادههای محصول را استخراج میکند. در انتها شما یک اسپایدر که فهرست محصولات را کشف میکند، صفحات محصول را پردازش میکند، دادهها را ذخیره میکند و چند نکته عملی برای عبور از محافظتهای ضدربات خواهید داشت.
آنچه یاد میگیرید:
برای اغلب پروژههای مانیتورینگ قیمت یا رتبهبندی، معماری پیشنهادی ساده و مؤثر شامل دو وظیفه اصلی است: یک product discovery crawler که URLهای محصولات را از صفحات جستجو پیدا میکند، و یک product data scraper که صفحات محصول را باز کرده و دادهها را استخراج میکند. این جداسازی به شما اجازه میدهد میزان درخواست به صفحات محصول را کنترل کنید و مجدداً URLها را از منابع دیگر (مثلاً لیست ASIN) تغذیه کنید.
مولفههای کلیدی:
URL جستجوی آمازون معمولاً پارامترها مثل k (keyword) و page دارد؛ مثال ساده:
https://www.amazon.com/s?k=ipad&page=1هر صفحه معمولاً تا حدود 20 محصول نشان میدهد. دو رویکرد برای فهرست محصولات وجود دارد:
استفاده از ASINها مزیت ثبات URL را دارد (کمتر پارامتر اضافه در URL) و گاهی برای بازیابی سریع صفحات محصول یا نظرات مناسبتر است.
ایده کلی: از start_requests برای ایجاد درخواست به صفحه اول هر کلیدواژه استفاده میکنیم، سپس در callback فهرست محصولات صفحه را میگیریم و درخواست برای صفحات بعد را زمانبندی میکنیم.
import scrapy
from urllib.parse import urljoin
class AmazonSearchProductSpider(scrapy.Spider):
name = "amazon_search_product"
def start_requests(self):
keyword_list = ['ipad']
for keyword in keyword_list:
url = f'https://www.amazon.com/s?k={keyword}&page=1'
yield scrapy.Request(url=url, callback=self.discover_product_urls, meta={'keyword': keyword, 'page': 1})
def discover_product_urls(self, response):
page = response.meta['page']
keyword = response.meta['keyword']
# استخراج محصولات از نتایج جستجو
search_products = response.css("div.s-result-item[data-component-type=s-search-result]")
for product in search_products:
rel = product.css("a::attr(href)").get()
if not rel:
continue
product_url = urljoin('https://www.amazon.com/', rel).split('?')[0]
# درخواست صفحه محصول و ارسال metadata مرتبط
yield scrapy.Request(url=product_url, callback=self.parse_product_data, meta={'keyword': keyword, 'page': page})
# اگر صفحه اول است، صفحات بعد را enqueue میکنیم (نمونه ساده)
if page == 1:
available_pages = response.xpath('//*[contains(@class, "s-pagination-item") and not(contains(@class, "s-pagination-separator"))]/text()').getall()
if available_pages:
last_page = available_pages[-1]
for page_num in range(2, int(last_page) + 1):
next_url = f'https://www.amazon.com/s?k={keyword}&page={page_num}'
yield scrapy.Request(url=next_url, callback=self.discover_product_urls, meta={'keyword': keyword, 'page': page_num})
شرح کد و ورودی/خروجی:
نکات عملی:
بعد از کشف URLها، برای هر صفحه محصول باید دادههای هدف (نام، قیمت، امتیاز، تعداد رأی، تصاویر، ویژگیها، و دادههای واریانت) را استخراج کنیم. در ادامه یک پیادهسازی نمونه و سپس توضیح آن آمده است.
import json
import re
import scrapy
from urllib.parse import urljoin
class AmazonSearchProductSpider(scrapy.Spider):
name = "amazon_search_product"
# ... start_requests و discover_product_urls مانند بالا ...
def parse_product_data(self, response):
# استخراج تصاویر که معمولاً در جاواسکریپت صفحه بهصورت JSON جاسازی شدهاند
image_json_match = re.findall(r"colorImages':.*'initial':\s*(\[.+?\])},\n", response.text)
image_data = []
if image_json_match:
try:
image_data = json.loads(image_json_match[0])
except Exception:
image_data = []
# مثال استخراج bullets
feature_bullets = [b.strip() for b in response.css("#feature-bullets li ::text").getall()]
# استخراج قیمت با چند selector احتمالی
price = response.css('.a-price span[aria-hidden="true"] ::text').get()
if not price:
price = response.css('.a-price .a-offscreen ::text').get()
product_data = {
"name": response.css("#productTitle::text").get(default="").strip(),
"price": price,
"stars": response.css("i[data-hook=average-star-rating] ::text").get(default="").strip(),
"rating_count": response.css("div[data-hook=total-review-count] ::text").get(default="").strip(),
"feature_bullets": feature_bullets,
"images": image_data,
}
self.logger.info(f"Scraped Data: {product_data}")
yield product_data
شرح:
نکات پایدارسازی:
سادهترین روش ذخیرهسازی در زمان توسعه، استفاده از Scrapy Feed Exports است. نمونه تنظیمات در settings.py:
# settings.py
FEEDS = {
'data/%(name)s_%(time)s.csv': {'format': 'csv'},
}
توضیح: با این تنظیم هر بار که اسپایدر اجرا شود یک فایل CSV جدید در پوشه data ایجاد میشود. برای ذخیره به S3 یا دیتابیس باید backend مناسب را تنظیم یا pipeline بنویسید.
مثایل جایگزین:
آمازون از تکنیکهای مختلف برای شناسایی رباتها استفاده میکند: تشخیص نرخ بالای درخواست، کپچا، بررسی هدرها و رفتار مرورگر. راهکارها به سه دسته کلی تقسیم میشوند:
نمونه سادهٔ CURL برای ارسال درخواست از طریق یک API پروکسی (نمونهٔ عمومی):
curl 'https://proxy.example.com/v1/?api_key=YOUR_API_KEY&url=https://amazon.com'
هشدارهای قانونی و اخلاقی: قبل از اسکریپ کردن مطمئن شوید قوانین سایت و سیاستهای استفاده را بررسی کردهاید. همیشه توجیۀ تجاری و اخلاقی داشته باشید و دادهپراکنی در مقیاس بزرگ بدون مجوز میتواند پیامد حقوقی داشته باشد.
چند تنظیم پیشنهادی در settings.py برای بهبود پایداری:
# settings.py (نمونه)
CONCURRENT_REQUESTS = 8
DOWNLOAD_DELAY = 1 # تاخیر بین درخواستها
RETRY_ENABLED = True
RETRY_TIMES = 3
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1
AUTOTHROTTLE_MAX_DELAY = 10
# User agents: لیستی از user-agent های چرخشی بهتر است در middleware مدیریت شود
توضیح: AUTOThrottle بهصورت داینامیک تاخیرها را تنظیم میکند تا سرور هدف کمتر تحت فشار قرار گیرد و احتمال بلاک کاهش یابد.
برای اجرا در تولید، حتماً مانیتورینگ داشته باشید: آمار اجرا، نرخ موفقیت درخواستها، زمان پاسخها و آلارم روی افزایش نرخ خطا یا CAPTCHA. میتوانید ابزارهای مانیتورینگ را بهعنوان Extension/Integration در Scrapy اضافه کنید یا لاگها را به یک سرویس خارجی ارسال کنید.
مواردی که باید پایش شوند:
برای اجرای منظم (روزانه/هفتگی) اسپایدرها، گزینههای متداول:
در هر حالت، لاگگیری مرکزی و عادت به ثبت خروجیها (مثلاً آپلود CSV به S3 یا ذخیره در DB) باعث تسهیل واکنش به خطاها میشود.
در این مقاله از طرح کلی معماری تا نمونه کدهای عملی برای ساخت یک اسپایدر Scrapy برای اسکریپ کردن محصولات آمازون گذر کردیم. نکات کلیدی عبارتند از: جداسازی discovery و scraping برای انعطافپذیری، مدیریت خطا و fallback برای پایداری، استفاده از Feeds برای ذخیره خروجی و در نهایت اجرای محافظهکارانه و استفاده از پروکسیها و مانیتورینگ برای ادامه کار در تولید. حالا میتوانید پیادهسازی را بسازید، آن را با تنظیمات و پروکسیهای مناسب مقاوم کنید و بهتدریج مقیاس دهید.