معرفی و پیشنیازها
Scrapy یک فریمورک متنباز پایتون برای وباسکرپینگ و خزیدن در وب است. با Scrapy میتوانید بهصورت ساختاریافته دادهها را از سایتها استخراج، پردازش و در فرمتهای مختلف ذخیره کنید.
پیشنیازها
- آشنایی با پایتون ۳.۸ یا بالاتر
- درک پایه HTML و CSS
- آشنایی با XPath یا CSS Selector (راهنمای XPath ما را ببینید)
- ترمینال و محیط مجازی (virtualenv)
معماری Scrapy
Scrapy از اجزای زیر تشکیل شده:
- Engine: هماهنگکننده مرکزی — درخواستها را مدیریت میکند
- Scheduler: صف درخواستها
- Downloader: دریافت صفحات وب
- Spider: منطق استخراج داده — کدی که شما مینویسید
- Item Pipeline: پردازش و ذخیره دادههای استخراجشده
- Middleware: پردازش درخواستها و پاسخها در مسیر
نصب
ایجاد محیط مجازی
python3 -m venv venv source venv/bin/activate # لینوکس/macOS venv\Scripts\activate # ویندوز
نصب Scrapy
pip install scrapy
بررسی نصب
scrapy version
ساخت پروژه
برای شروع یک پروژه جدید:
scrapy startproject myproject
ساختار پروژه:
myproject/ ├── scrapy.cfg # تنظیمات deploy └── myproject/ ├── __init__.py ├── items.py # تعریف Item ├── middlewares.py # Middleware سفارشی ├── pipelines.py # Pipeline سفارشی ├── settings.py # تنظیمات پروژه └── spiders/ └── __init__.py
ساخت Spider
scrapy genspider example example.com
اجرای Spider
scrapy crawl example
خروجی JSON:
scrapy crawl example -o output.json
Spider
Spider قلب Scrapy است — جایی که منطق استخراج داده را مینویسید.
هر Spider باید یک name یکتا و حداقل یک متد parse داشته باشد.
Spider ساده
import scrapy class QuotesSpider(scrapy.Spider): name = "quotes" start_urls = [ "https://quotes.toscrape.com/page/1/", ] def parse(self, response): for quote in response.css("div.quote"): yield { "text": quote.css("span.text::text").get(), "author": quote.css("small.author::text").get(), "tags": quote.css("div.tags a.tag::text").getall(), } next_page = response.css("li.next a::attr(href)").get() if next_page is not None: yield response.follow(next_page, callback=self.parse)
روشهای شروع Spider
start_urls— لیست URLهای اولیهstart_requests()— کنترل کامل درخواستهای اولیه- آرگومانهای خط فرمان با
-a
class MySpider(scrapy.Spider): name = "myspider" def __init__(self, category="", *args, **kwargs): super().__init__(*args, **kwargs) self.start_urls = [f"https://example.com/{category}"] # اجرا: scrapy crawl myspider -a category=books
پیمایش بین صفحات
# follow — دنبال کردن لینک yield response.follow("/page/2/", callback=self.parse) # follow_all — دنبال کردن همه لینکها yield from response.follow_all(links, callback=self.parse_detail) # Request دستی yield scrapy.Request(url, callback=self.parse_detail)
انتخابگرها (Selectors)
Scrapy از پارسر lxml استفاده میکند و دو نوع انتخابگر دارد:
CSS و XPath. هر دو از طریق شیء response در دسترساند.
CSS Selector
response.css("title::text").get() # اولین نتیجه response.css("title::text").getall() # همه نتایج response.css("img::attr(src)").get() # مقدار ویژگی response.css("div.quote").get() # HTML کامل
XPath
response.xpath("//title/text()").get() response.xpath('//span[@class="text"]/text()').getall() response.xpath('//a/@href').get()
برای راهنمای کامل XPath به صفحه آموزش XPath مراجعه کنید.
نکات انتخابگر
.get()اولین نتیجه یاNoneبرمیگرداند.getall()لیست همه نتایج را برمیگرداند- برای انتخاب زیرمجموعه از
.css()یا.xpath()روی نتیجه قبلی استفاده کنید ::textبرای متن و::attr(name)برای ویژگی در CSS
Item و Item Loader
تعریف Item
در فایل items.py:
import scrapy class ProductItem(scrapy.Item): name = scrapy.Field() price = scrapy.Field() url = scrapy.Field() description = scrapy.Field()
استفاده در Spider
from myproject.items import ProductItem def parse(self, response): item = ProductItem() item["name"] = response.css("h1::text").get() item["price"] = response.css(".price::text").get() item["url"] = response.url yield item
Item Loader
برای پردازش و تمیز کردن داده قبل از ذخیره:
from scrapy.loader import ItemLoader from myproject.items import ProductItem loader = ItemLoader(item=ProductItem(), selector=response.css("div.product")) loader.add_css("name", "h2::text") loader.add_css("price", "span.price::text") loader.add_value("url", response.url) yield loader.load_item()
Pipeline
Pipelineها دادههای استخراجشده را پردازش میکنند — تمیز کردن، اعتبارسنجی، حذف تکراری و ذخیره در فایل یا دیتابیس.
ساخت Pipeline
در فایل pipelines.py:
import json class JsonWriterPipeline: def open_spider(self, spider): self.file = open("items.jsonl", "w", encoding="utf-8") def close_spider(self, spider): self.file.close() def process_item(self, item, spider): line = json.dumps(dict(item), ensure_ascii=False) + "\n" self.file.write(line) return item
فعالسازی Pipeline
در settings.py:
ITEM_PIPELINES = {
"myproject.pipelines.JsonWriterPipeline": 300,
}عدد کوچکتر = اولویت بالاتر. Pipelineها به ترتیب اولویت اجرا میشوند.
مثال: حذف تکراری
class DuplicatesPipeline: def __init__(self): self.ids_seen = set() def process_item(self, item, spider): if item["id"] in self.ids_seen: raise DropItem(f"Duplicate item: {item['id']}") self.ids_seen.add(item["id"]) return item
Middleware
Middlewareها درخواستها و پاسخها را قبل و بعد از Downloader پردازش میکنند.
انواع Middleware
- Downloader Middleware: پردازش Request/Response — User-Agent، پروکسی، Retry
- Spider Middleware: پردازش ورودی/خروجی Spider
مثال: User-Agent تصادفی
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware import random class RandomUserAgentMiddleware(UserAgentMiddleware): user_agent_list = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...", ] def process_request(self, request, spider): request.headers["User-Agent"] = random.choice(self.user_agent_list)
فعالسازی در settings.py:
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.RandomUserAgentMiddleware": 400,
}تنظیمات مهم
فایل settings.py رفتار Scrapy را کنترل میکند:
# رعایت robots.txt ROBOTSTXT_OBEY = True # تأخیر بین درخواستها (ثانیه) DOWNLOAD_DELAY = 2 # حداکثر درخواست همزمان به یک دامنه CONCURRENT_REQUESTS_PER_DOMAIN = 2 # User-Agent پیشفرض USER_AGENT = "mybot (+http://www.example.com)" # فعالسازی AutoThrottle AUTOTHROTTLE_ENABLED = True AUTOTHROTTLE_START_DELAY = 1 AUTOTHROTTLE_MAX_DELAY = 10 # Cache HTTPCACHE_ENABLED = True HTTPCACHE_EXPIRATION_SECS = 86400 # Retry RETRY_TIMES = 3 RETRY_HTTP_CODES = [500, 502, 503, 504, 408]
تنظیمات از خط فرمان
scrapy crawl myspider -s DOWNLOAD_DELAY=3 -s LOG_LEVEL=INFO
CrawlSpider
برای سایتهایی با ساختار لینک منظم، CrawlSpider با قوانین
Rule پیمایش را خودکار میکند.
from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor class MyCrawlSpider(CrawlSpider): name = "crawl_example" allowed_domains = ["example.com"] start_urls = ["https://example.com"] rules = ( Rule(LinkExtractor(allow=r"/category/\d+"), callback="parse_category"), Rule(LinkExtractor(allow=r"/product/\d+"), callback="parse_product"), ) def parse_product(self, response): yield { "title": response.css("h1::text").get(), "price": response.css(".price::text").get(), }
بهترین شیوهها
اخلاق و قانون
- همیشه
robots.txtرا رعایت کنید - بین درخواستها تأخیر بگذارید (
DOWNLOAD_DELAY) - User-Agent معتبر و شناساییپذیر تنظیم کنید
- بار سرور هدف را در نظر بگیرید
کیفیت کد
- هر Spider یک مسئولیت مشخص داشته باشد
- از Item بهجای دیکشنری ساده استفاده کنید
- دادهها را در Pipeline تمیز و اعتبارسنجی کنید
- لاگها را برای دیباگ فعال نگه دارید:
LOG_LEVEL = "DEBUG" - از
scrapy shellبرای تست انتخابگرها استفاده کنید
ابزارهای مفید
# Shell تعاملی برای تست scrapy shell "https://example.com" # در shell: response.css("h1::text").get() response.xpath("//title/text()").get() # لیست Spiderها scrapy list # بررسی قراردادها scrapy check
خروجی داده
scrapy crawl myspider -o data.json # JSON scrapy crawl myspider -o data.csv # CSV scrapy crawl myspider -o data.xml # XML scrapy crawl myspider -o data.jsonl # JSON Lines
سوالات متداول
چطور JavaScript را رندر کنم؟
Scrapy بهتنهایی JavaScript اجرا نمیکند. از افزونههایی مثل scrapy-playwright یا scrapy-splash استفاده کنید.
چطور لاگین کنم؟
با FormRequest.from_response() فرم لاگین را ارسال کنید
یا از cookies و session استفاده کنید.
yield scrapy.FormRequest.from_response( response, formdata={"username": "user", "password": "pass"}, callback=self.after_login )
چطور پروکسی استفاده کنم؟
در Middleware یا مستقیماً در Request:
yield scrapy.Request(
url,
meta={"proxy": "http://proxy.example.com:8080"},
callback=self.parse
)خطای ۴۰۳ یا بلاک شدن
- User-Agent واقعی تنظیم کنید
- تأخیر بین درخواستها را افزایش دهید
- از پروکسی چرخشی استفاده کنید
- هدرهای HTTP واقعیتر ارسال کنید