مقدمه
در این مقاله یاد میگیریم چگونه خروجیهای یک ربات Scrapy را به صورت CSV ذخیره کنیم و چه گزینهها و تنظیماتی برای مدیریت مسیرها، رمزگذاری، دستهبندی فایلها و امنسازی دادهها در دسترس است. مخاطب این مطلب توسعهدهندهای است که با پایتون و Scrapy آشناست و دنبال توضیحات دقیق، مثالهای کد و نکات عملی برای تولید، سازماندهی و نگهداری خروجیهای CSV میگردد.
Feed Exporters در Scrapy چیست و چه کاری انجام میدهد؟
بهصورت خلاصه، Feed Exporters مجموعهای از سازوکارها در Scrapy است که خروجی آیتمها را به فرمتهای مختلف (مثل CSV، JSON، XML و حتی pickle) صادر (export) میکند و میتواند آنها را روی مکانهای مختلف (فایل محلی، FTP، S3، GCS، یا استاندارد خروجی) قرار دهد. این سیستم مدیریت نوشتن و قالببندی خروجی را استاندارد و قابل پیکرهبندی میکند.
چرا از Feed Exporters استفاده کنیم؟
- عدم نیاز به پیادهسازی دستی فرمت CSV و مدیریت هدرها.
- پشتیبانی از مکان ذخیرهسازی متنوع و پیکربندی مرکزی.
- گزینههایی برای فیلتر کردن فیلدها، کلاس آیتمها، و پسپردازش.
ذخیرهسازی CSV از طریق خط فرمان
- -o: خروجی را به فایل مشخص اضافه (append) میکند.
- -O: فایل موجود را بازنویسی (overwrite) میکند.
مثالهای عملی (در ترمینال):
# مسیر نسبی و اضافه کردن به فایل موجود
scrapy crawl bookspider -o bookspider_data.csv
# مسیر مطلق (schema file:// برای اطمینان از استفاده از فایل محلی)
scrapy crawl bookspider -o file:///path/to/project/bookspider_data.csv
# بازنویسی کامل فایل
scrapy crawl bookspider -O results.csvچه وقت از این روش استفاده کنیم؟ برای توسعه سریع یا اجراهای ad-hoc مناسب است اما برای محیطهای تولیدی پیکربندیشده و قابل تکرار، FEEDS در فایل تنظیمات یا تنظیمات اختصاصی هر اسپایدر تمیزتر و قابل نگهداریتر است.
استفاده از تنظیمات FEEDS در settings.py
تعریف FEEDS در settings.py کنترل متمرکز بر نحوه و مکان ذخیرهسازی خروجی را فراهم میکند. ساختار پایه یک ورودی FEEDS به صورت یک دیکشنری است که کلید آن URI فایل و مقدار آن یک دیکشنری از گزینههاست.
# settings.py
FEEDS = {
'data/data.csv': {
'format': 'csv',
}
}توضیح ورودیها و خروجیها:
- کلید دیکشنری: رشتهای که نشاندهنده مکان خروجی (فایل محلی، s3://، gs://، ftp:// و ...) است.
- 'format': فرمت خروجی (مثلاً 'csv' یا 'json').
میتوان در هر اسپایدر نیز تنظیمات سفارشی گذاشت با استفاده از custom_settings تا مشخصات FEEDS مخصوص آن اسپایدر اعمال شود.
# bookspider.py
import scrapy
from myproject.items import BookItem
class BookSpider(scrapy.Spider):
name = 'bookspider'
start_urls = ['http://books.toscrape.com']
custom_settings = {
'FEEDS': {
'data/bookspider.csv': {'format': 'csv'},
}
}
def parse(self, response):
# ورودی: response از صفحه
# خروجی: yield کردن BookItem برای صادر شدن توسط Feed Exporter
for article in response.css('article.product_pod'):
book_item = BookItem(
url=article.css('h3 > a::attr(href)').get(),
title=article.css('h3 > a::attr(title)').get(),
price=article.css('.price_color::text').get(),
)
yield book_itemدر این مثال:
- هر آیتمی که yield میشود توسط Feed Exporter گرفته و به فرمت CSV تبدیل میشود.
- نقش parse تولید آیتمها و آمادهسازی داده برای صادرات است.
تنظیم گزینههای اضافی در FEEDS
FEEDS میتواند گزینههای متنوعی بپذیرد که کنترل بیشتری روی خروجی میدهد. مهمترین گزینهها عبارتند از:
- encoding: رمزگذاری فایل خروجی؛ پیشفرض UTF-8 است (برای JSON رفتار متفاوتی دارد).
- fields: لیستی از فیلدها برای صادر شدن (برای کنترل ترتیب ستونها یا حذف ستونها).
- item_classes: فقط آیتمهایی از این کلاسها صادر شوند.
- store_empty: آیا فایل خالی را صادر کند یا نه.
- item_export_kwargs: مثلاً {'export_empty_fields': True} تا فیلدهای خالی هم صادر شوند.
- postprocessing: لیستی از پلاگینها برای پردازش بعد از نوشتن.
مثال ترکیبی در settings:
FEEDS = {
'data/%(name)s/%(name)s_%(time)s.csv': {
'format': 'csv',
'encoding': 'utf8',
'store_empty': False,
'fields': None,
'item_export_kwargs': {'export_empty_fields': True},
}
}توضیح پارامترهای دینامیک: %(name)s و %(time)s در زمان ایجاد فید با مقدارهای مربوطه جایگزین میشوند؛ این امکان برای ساخت مسیرهای منظم و با تاریخ مناسب بسیار مفید است.
مسیرها و نام فایلهای پویا
برای پروژههای بزرگ و زمانبندیشده، جمعآوری همه دادهها در یک فایل بزرگ مشکلساز است. Scrapy اجازه میدهد با استفاده از placeholderها مسیرها و نامها را پویا کنید. مثال:
FEEDS = {
'data/%(name)s/%(name)s_%(time)s.csv': {'format': 'csv'}
}
# مثال تولیدی: data/bookspider/bookspider_2022-05-18T07-47-03.csvهر placeholder که با %(...)s یا %(...)d نوشته شود، با صفت متناظر اسپایدر (مثل spider.name یا مقدار زمان) جایگزین میشود. میتوانید placeholderهای سفارشی هم استفاده کنید مشروط بر اینکه در زمان ایجاد فید attribute مربوطه در اسپایدر موجود باشد.
تقسیم خروجی به فایلهای دستهای (batching)
اگر میخواهید هنگام اجرا خروجی به چند فایل تقسیم شود (برای مدیریت حجم یا پردازش موازی)، از کلید batch_item_count استفاده کنید. مقدار آن عددی است که مشخص میکند حداکثر چند آیتم در هر فایل قرار گیرد.
FEEDS = {
'data/%(name)s/%(name)s_batch_%(batch_id)d.csv': {
'format': 'csv',
'batch_item_count': 100,
}
}
# تولید فایلها: bookspider_batch_1.csv, bookspider_batch_2.csv, ...نکته مهم: برای نامگذاری دستهای باید حداقل یکی از placeholderهای %(batch_time)s یا %(batch_id)d را در URI قرار دهید تا Scrapy بتواند اسامی فایلها را متمایز کند.
نکات امنیتی، سازگاری و بهترینروشها
چند نکته عملی که در تولید و نگهداری CSVها باید رعایت کنید:
- احراز هویت و نگهداری کلیدها: هنگام نوشتن به سرویسهای ابری (S3/GCS/FTP) از متغیرهای محیطی برای نگهداری credentials استفاده کنید و آنها را در کد صریح قرار ندهید.
- جلوگیری از CSV Injection: مقادیر ورودی که با کاراکترهای =, +, -, @ شروع میشوند ممکن است در اکسل یا نرمافزارهای صفحهگسترده اجرا شوند. آنها را قبل از نوشتن پاک یا علامتگذاری کنید. مثال تابع ساده برای پاکسازی:
def sanitize_cell(value):
# ورودی: مقدار رشتهای سلول
# خروجی: مقدار امن برای نوشتن در CSV
if not isinstance(value, str):
return value
if value and value[0] in ('=', '+', '-', '@'):
return "'" + value # اضافه کردن آپاستروف باعث میشود اکسل آن را literal بخواند
return value- کنترل encoding: همیشه encoding را مشخص کنید (مثلاً 'utf8') تا مشکلات کاراکترهای غیرلاتین پیش نیاید.
- مدیریت خطا و پایداری: از middlewares و retryهای Scrapy برای اطمینان از جمعآوری کامل دادهها استفاده کنید و خروجی را در حالت تست چندباره بررسی کنید.
- عملکرد: برای مجموعههای بزرگ، استفاده از batch_item_count و ذخیرهسازی به سرویسهای ابری مناسبتر از یک فایل تک بزرگ است. همچنین توجه کنید که نوشتن همزمان به remote storage میتواند گلوگاه شبکه ایجاد کند.
- حذف فایلهای قدیمی: اگر overwrite را True قرار میدهید، مراقب باشید که اطلاعات قبلی از بین نرود؛ در حالت محلی پیشفرض برای بعضی استورها overwrite برابر False است.
مدیریت پسپردازش و فیلدها
با استفاده از گزینههایی مثل postprocessing میتوانید پلاگینهایی اضافه کنید که بعد از ایجاد فید روی فایلها عملیات انجام دهند (مثلاً فشردهسازی، آپلود ثانویه، یا تبدیل قالب). همچنین اگر میخواهید ترتیب ستونها مشخص باشد از گزینه fields استفاده کنید تا ستونها به صورت پایدار در فایل CSV تولید شوند.
جمعبندی و توصیههای نهایی
خلاصهٔ راهکارها:
- برای کارهای سریع و آزمایشی از فلگهای خط فرمان -o/-O استفاده کنید.
- برای پروژههای تولیدی FEEDS در settings.py یا custom_settings هر اسپایدر گزینه مناسب و قابل نگهداری است.
- از placeholderها برای مسیرها و نام فایل پویا استفاده کنید و برای مدیریت حجم از batch_item_count بهره ببرید.
- همیشه encoding را تنظیم کنید و از مکانیزمهای جلوگیری از CSV injection و محافظت از credentials استفاده کنید.
با رعایت این نکات، شما میتوانید خروجیهای Scrapy را به صورت پایدار، امن و قابل پردازش تولید کنید و جریان دادهای ساختاریافتهای برای تحلیلهای بعدی یا بارگذاری به پایگاه داده و سرویسهای ابری داشته باشید.





