خانه/مقالات/اسکریپینگ Walmart با Scrapy: راهنمای عملی
پروکسی و چرخش IP
ضد بلاک (Anti-bot)
Headless Chrome
برگشت به صفحه مقاله ها
اسکریپینگ Walmart با Scrapy: راهنمای عملی

اسکریپینگ Walmart با Scrapy: راهنمای عملی

این راهنما نشان می‌دهد چگونه با Scrapy یک اسکریپر عملی برای Walmart بسازید: طراحی معماری discovery + product scraper، استخراج JSON از تگ __NEXT_DATA__, صفحه‌بندی و محدودیت 25 صفحه، ذخیره‌سازی با FEEDS یا پایپلاین، و روش‌های مقابله با محافظت ضد-ربات مثل پراکسی چرخشی و headless browser. همچنین نکات مربوط به مانیتورینگ، بهترین‌روش‌های عملی و استقرار در محیط تولید پوشش داده شده‌اند.
آسان اسکریپ
آسان اسکریپ
1404-12-07

مقدمه

در این مقاله گام‌به‌گام می‌فهمیم چگونه با Scrapy یک اسکریپر عملی برای Walmart بسازیم. هدف این راهنما برای یک توسعه‌دهندهٔ پایتون سطح متوسط است که می‌خواهد از طراحی معماری تا ذخیره‌سازی، مقابله با محافظت ضد ربات و استقرار را یاد بگیرد. در پایان این مطلب شما یک اسپایدر کشف محصولات (crawler) و یک اسکریپر صفحات محصول خواهید داشت، می‌دانید چطور داده‌ها را ذخیره کنید و چه روش‌هایی برای پایداری و عبور از محدودیت‌ها استفاده کنید.

معماری پیشنهادی

برای بیشتر موارد عملی، ترکیب دو مولفه ساده و قابل نگهداری کافی است:

  • یک محصول‌یاب (discovery crawler) که صفحات جستجو را می‌خزد و فهرست URL محصولات می‌سازد.
  • یک اسکریپر محصول (product scraper) که صفحهٔ هر محصول را باز کرده و دادهٔ لازم را از یک بلوک JSON استخراج می‌کند.

تصمیم‌های مهم معماری بر اساس این پرسش‌ها گرفته می‌شوند: محدودهٔ داده، نرخ به‌روزرسانی، حجم داده و توان فنی. برای مثال این راهنما فرض می‌کند حجم متوسط و نیاز روزانه وجود دارد و خروجی را به‌صورت CSV (یا از طریق FEEDS به S3/DB) ذخیره می‌کنیم.

ساخت crawler جستجو در Walmart

Walmart نتایج جستجو را با پارامترهای ساده در URL ارائه می‌دهد: q (کلمهٔ جستجو)، sort و page. هر صفحه تا 40 محصول بازمی‌گرداند و حداکثر 25 صفحه قابل دسترسی است—پس باید تعداد صفحات را محاسبه و صفحه‌بندی کنیم.

نکتهٔ کلیدی: داده‌ها در تگی با id __NEXT_DATA__ به‌صورت JSON توکار قرار دارند. بهترین روش خواندن همین JSON و استخراج مسیر مناسب است.

import json
import math
import scrapy
from urllib.parse import urlencode

class WalmartSpider(scrapy.Spider):
    name = "walmart"

    def start_requests(self):
        # ورودی: لیست کلمات کلیدی
        keyword_list = ["ipad"]
        for keyword in keyword_list:
            payload = {"q": keyword, "sort": "best_seller", "page": 1, "affinityOverride": "default"}
            walmart_search_url = "https://www.walmart.com/search?" + urlencode(payload)
            # خروجی: درخواست به صفحهٔ اول نتایج
            yield scrapy.Request(url=walmart_search_url, callback=self.parse_search_results, meta={"keyword": keyword, "page": 1})

    def parse_search_results(self, response):
        page = response.meta["page"]
        keyword = response.meta["keyword"]
        script_tag = response.xpath('//script[@id="__NEXT_DATA__"]/text()').get()
        if script_tag is not None:
            json_blob = json.loads(script_tag)
            # ادامهٔ پردازش در بخش‌های بعدی

توضیح مختصر: ورودی تابع start_requests لیست کلمات کلیدی است؛ هر درخواست به parse_search_results فرستاده می‌شود که متن تگ اسکریپت را می‌خواند و آن‌را به JSON تبدیل می‌کند.

صفحات جستجو: استخراج لیست محصولات و صفحه‌بندی

پس از بارگذاری JSON، مسیر دادهٔ نتایج معمولاً چیزی شبیه به این است (نمونه مسیر):

json_blob["props"]["pageProps"]["initialData"]["searchResult"]["itemStacks"][0]["items"]

  1. از JSON بالا product_list را بگیرید.
  2. از هر آیتم آدرس canonical یا مسیر محصول را استخراج کنید.
  3. اگر صفحهٔ اول است، تعداد کل محصولات را بخوانید، تعداد صفحات را محاسبه کنید (هر صفحه 40 محصول) و اگر بیش از 25 صفحه شد، تا 25 صفحه محدود کنید.
            # ادامهٔ داخل parse_search_results
            product_list = json_blob["props"]["pageProps"]["initialData"]["searchResult"]["itemStacks"][0]["items"]

            # استخراج آدرس محصول و درخواست صفحهٔ محصول
            for product in product_list:
                walmart_product_url = "https://www.walmart.com" + product.get("canonicalUrl", "").split("?")[0]
                yield scrapy.Request(url=walmart_product_url, callback=self.parse_product_data, meta={"keyword": keyword, "page": page})

            # صفحه‌بندی (فقط از صفحهٔ اول تعداد کل را می‌خوانیم)
            if page == 1:
                total_product_count = json_blob["props"]["pageProps"]["initialData"]["searchResult"]["itemStacks"][0].get("count", 0)
                max_pages = math.ceil(total_product_count / 40)
                if max_pages > 25:
                    max_pages = 25
                for p in range(2, max_pages + 1):
                    payload = {"q": keyword, "sort": "best_seller", "page": p, "affinityOverride": "default"}
                    walmart_search_url = "https://www.walmart.com/search?" + urlencode(payload)
                    yield scrapy.Request(url=walmart_search_url, callback=self.parse_search_results, meta={"keyword": keyword, "page": p})

نکتهٔ عملی: حتماً پارامتر جستجو را URL-encode کنید و برای جلوگیری از تکرار URLها canonicalUrl را قبل از '?' برش بزنید.

نوشتن callback برای صفحۀ محصول

پس از درخواست هر صفحهٔ محصول، به‌جای ساخت سلکتورهای CSS/XPath برای تک‌تک فیلدها بهتر است همان JSON توکار (__NEXT_DATA__) را ببرید و از مسیر محصول استفاده کنید. مسیر معمول برای دادهٔ محصول:

json_blob["props"]["pageProps"]["initialData"]["data"]["product"]

def parse_product_data(self, response):
    script_tag = response.xpath('//script[@id="__NEXT_DATA__"]/text()').get()
    if script_tag is None:
        return
    json_blob = json.loads(script_tag)
    raw_product_data = json_blob["props"]["pageProps"]["initialData"]["data"].get("product", {})

    # خروجی: دیکشنری قابل ذخیره
    item = {
        'keyword': response.meta.get('keyword'),
        'page': response.meta.get('page'),
        'id': raw_product_data.get('id'),
        'type': raw_product_data.get('type'),
        'name': raw_product_data.get('name'),
        'brand': raw_product_data.get('brand'),
        'averageRating': raw_product_data.get('averageRating'),
        'manufacturerName': raw_product_data.get('manufacturerName'),
        'shortDescription': raw_product_data.get('shortDescription'),
        'thumbnailUrl': (raw_product_data.get('imageInfo') or {}).get('thumbnailUrl'),
        'price': (raw_product_data.get('priceInfo') or {}).get('currentPrice', {}).get('price'),
        'currencyUnit': (raw_product_data.get('priceInfo') or {}).get('currentPrice', {}).get('currencyUnit')
    }
    yield item

توضیح: این تابع یک item تولید می‌کند که قابل ارسال به pipeline یا Scrapy FEEDS است. توجه کنید که فیلدهای توخالی را با گاردهای ساده (مثل .get و OR {}) محافظت کرده‌ایم تا خطاهای KeyError کاهش یابد.

ذخیره‌سازی داده‌ها (CSV، DB یا S3)

برای خروجی سریع و قابل تحلیل، از FEEDS در Scrapy استفاده کنید. این تنظیم یک فایل CSV با نام داینامیک بر اساس نام اسپایدر و زمان اجرا ایجاد می‌کند.

# settings.py
FEEDS = {
    'data/%(name)s_%(time)s.csv': {
        'format': 'csv',
    },
}

برای اجرای اسپایدر از خط فرمان:

scrapy crawl walmart -o walmart_data.csv

اگر می‌خواهید به پایگاه داده بنویسید، یک pipeline بسازید که اتصال به MySQL/Postgres را مدیریت کند و هر آیتم را داخل جدول مربوطه درج نماید. مزیت استفاده از پایپلاین: جمع‌آوری خطا و بازسازی سریع داده‌های نیمه‌کامل.

مقابله با محافظت ضد-ربات (Anti-bot)

Walmart دارای محافظت‌هایی است که در صورت ارسال درخواست‌های زیاد ممکن است شما را بلوک کنند. ترکیبی از روش‌های زیر به افزایش پایداری کمک می‌کند:

  • استفاده از پراکسی‌های چرخشی (rotating proxies)
  • تغییر منظم هدرها و User-Agent
  • استفاده از headless browser برای رندر جاوااسکریپت در مواقع لازم
  • تشخیص CAPTCHA و fallback به سرویس‌های حل کپچا یا صف‌بندی کار
  • محدودسازی نرخ درخواست و backoff تدریجی هنگام خطا

مثال: یک API پراکسی که آدرس را می‌گیرد و صفحهٔ نهایی را برمی‌گرداند (curl نمونه):

curl 'https://proxy.example.com/v1/?api_key=YOUR_API_KEY&url=https://walmart.com'

برای ادغام سریع می‌توانید از SDK پراکسی به‌صورت Downloader Middleware استفاده کنید. مثال نصب و تنظیم (نمونهٔ کلی):

pip install scrapeops-scrapy-proxy-sdk
# settings.py
SCRAPEOPS_API_KEY = 'YOUR_API_KEY'
SCRAPEOPS_PROXY_ENABLED = True
DOWNLOADER_MIDDLEWARES = {
    'scrapeops_scrapy_proxy_sdk.scrapeops_scrapy_proxy_sdk.ScrapeOpsScrapyProxySdk': 725,
}

هشدار امنیتی و اخلاقی: قبل از اسکریپ کردن هر سایت، قوانین سرویس، شرایط استفاده و قوانین محلی را بررسی کنید و بارگزاری روی سرور را تا حد امکان نرم و آگاهانه انجام دهید.

نظارت و پایش (Monitoring)

چند معیار مهم برای پایش عملیاتی: نرخ خطا، تعداد درخواست‌ها، میانگین زمان پاسخ، تعداد CAPTCHAها و نرخ موفقیت ذخیره‌سازی. ابزارهای مانیتورینگ مخصوص Scrapy می‌توانند این متریک‌ها را به داشبورد بفرستند و هشدار دهی روشن تنظیم کنند.

# settings.py (نمونهٔ ادغام با ScrapeOps Monitor)
EXTENSIONS = {
    'scrapeops_scrapy.extension.ScrapeOpsMonitor': 500,
}
DOWNLOADER_MIDDLEWARES.update({
    'scrapeops_scrapy.middleware.retry.RetryMiddleware': 550,
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,
})
SCRAPEOPS_API_KEY = 'YOUR_API_KEY'

با افزودن مانیتور و middleware مناسب می‌توانید خطاهای خودکار، retry هوشمند و گزارش رویدادها را مدیریت کنید.

استقرار و زمانبندی در محیط تولید

گزینه‌های معمول برای اجرا و زمانبندی:

  • استفاده از کران یا systemd روی یک سرور VPS/VM
  • کانتینریزه کردن با Docker و اجرای زمانبندی‌شده در اورکستریتور
  • استفاده از سرویس‌های Job Scheduler که اجرای دوره‌ای و لاگ‌برداری را ساده می‌کنند

در محیط تولید حتماً موارد زیر را رعایت کنید: محدودیت نرخ، بازهٔ retry، مدیریت لاگ‌ها و مکانیزم ری‌‌استارت خودکار برای اسپایدرها.

نکات عملکردی و بهینه‌سازی

  • درخواست‌ها را به‌صورت همزمان با محدودیت معقول ارسال کنید (تنظیم CONCURRENT_REQUESTS و DOWNLOAD_DELAY).
  • از کش محلی برای صفحاتی که عوض نمی‌شوند استفاده کنید.
  • در صورت نیاز به حجم بالا، تقسیم وظایف به صف‌های مستقل (مثلاً جدا کردن discovery و scraping) کارآمدتر و پایدارتر است.

جمع‌بندی

پیاده‌سازی یک اسکریپر Walmart با Scrapy شامل سه بخش اصلی است: کشف URLها از صفحات جستجو، استخراج داده از بلوک JSON صفحات محصول و ذخیرهٔ منظم نتایج. برای تولیدی‌سازی پروژه باید به ذخیره‌سازی مناسب، مدیریت محافظت ضد-ربات و نظارت پرداخته و از بهترین روش‌های retry، backoff و پراکسی چرخشی استفاده کنید. قدم بعدی: پیاده‌سازی pipeline برای ذخیره در DB، اضافه‌کردن لاگ و مانیتورینگ کامل و استقرار در محیطی با زمانبندی اتوماتیک.

مقاله‌های مرتبط
ابزارها و فریم‌ورک‌ها (Scrapy, Puppeteer و …)
1404-12-01
نظارت اسپایدرهای Scrapy در وب اسکریپینگ
این راهنما چهار روش نظارت روی اسپایدرهای Scrapy را بررسی می‌کند: لاگ‌ها و آمار داخلی، ابزارهای اختصاصی مانیتورینگ، Spidermon برای تست‌های اعتبارسنجی و ابزارهای عمومی لاگینگ. با مثال‌های پایتون و تنظیمات عملی، توصیه‌های استقرار و یک چک‌لیست عملی برای تولید ارائه شده است.
ابزارها و فریم‌ورک‌ها (Scrapy, Puppeteer و …)
1404-11-28
مانیتورینگ Scrapy با Spidermon برای وب اسکریپینگ
این مقاله راهنمایی عملی برای راه‌اندازی Spidermon در پروژه‌های Scrapy ارائه می‌دهد: از نصب و تنظیمات پایه تا نوشتن Monitorها، MonitorSuiteها، اعتبارسنجی آیتم‌ها و ارسال نوتیفیکیشن (مثلاً اسلک). با مثال‌های کد پایتون و توضیحات خط به خط، می‌توانید ظرف چند دقیقه یک مانیتورینگ قابل اتکا برای وب اسکریپینگ خود بسازید.
ابزارها و فریم‌ورک‌ها (Scrapy, Puppeteer و …)
1404-11-27
Scrapyd؛ راهنمای عملی اسکریپ با Scrapy
این مقاله یک راهنمای عملی برای نصب، استقرار و مدیریت اسپایدرهای Scrapy با Scrapyd ارائه می‌دهد؛ شامل مثال‌های کد برای deploy، استفاده از API و کتابخانهٔ python-scrapyd-api، ادغام با ScrapeOps و نکات امنیتی و بهترین‌روش‌ها برای اجرا در محیط تولید.