مقدمه
در این مقاله عملی یاد میگیریم چطور با Scrapy به صفحات لاگین وارد شویم و دادههای پشت احراز هویت را استخراج کنیم. ابتدا روششناسی تحلیل فرم لاگین را توضیح میدهیم، سپس سه روش رایج (ارسال فرم ساده، ارسال فرم به همراه دادههای پنهان و استفاده از مرورگر headless) را با مثالهای پایتون و نکات فنی پوشش میدهیم. در انتها بهترین روشها برای کاهش ریسک بلاک شدن و ملاحظات حقوقی را مرور میکنیم.
گام اول: تحلیل فرایند لاگین
قبل از نوشتن هر کدی باید فرایند لاگین سایت هدف را با ابزارهای توسعهدهنده مرورگر بررسی کنید. مراحل کلی:
- مشاهده آدرس (URL) ای که درخواست لاگین را دریافت میکند.
- بررسی Payload (دادههای ارسالشده) در درخواست POST یا XHR.
- بررسی هدرها و کوکیهایی که درخواست لاگین نیاز دارد.
هدف این مرحله: تعیین این که آیا سایت فقط نامکاربری/رمز عبور میخواهد، یا توکنهای پنهان (مثل CSRF) یا دادههای تولیدشده سمت کلاینت و حتی رمزنگاریشده دارد.
روش ۱: ارسال ساده با FormRequest
ایده کلی: اگر سایت صرفاً فیلدهای نامکاربری و رمز عبور را دریافت میکند، کافی است یک درخواست POST مشابه فرم ارسال کنیم. این روش سریعترین و سادهترین است اما این روزها کم پیش میآید که تنها همین دادهها لازم باشند.
مثال ساده (ورودیها: آدرس لاگین، ایمیل و پسورد — خروجی: callback برای ادامهٔ اسکریپ):
from scrapy import Spider
from scrapy.http import FormRequest
class SimpleLoginSpider(Spider):
name = 'simple_login'
def start_requests(self):
login_url = 'http://example.com/login'
# formdata: دیکشنری فیلدهای فرم
yield FormRequest(login_url, formdata={'email': 'you@example.com', 'password': 'secret'}, callback=self.start_scraping)
def start_scraping(self, response):
# response مربوط به صفحه بعد از لاگین است
# از اینجا scraping صفحات پشت لاگین را آغاز کنید
pass
چه کار میکند (خطبهخط):
- start_requests: درخواست ابتدایی به صفحهٔ لاگین میفرستد.
- FormRequest: دادههای فرم را با روش POST ارسال میکند و جلسه (cookies) را درون Scrapy مدیریت میکند.
- start_scraping: callback که پس از لاگین فراخوانی میشود و ادامهٔ خزیدن را انجام میدهد.
محدودیتها: اگر سایت نیاز به CSRF یا دادههای پنهان یا رمزنگاری سمت کلاینت داشته باشد این روش ناکافی است.
روش ۲: FormRequest با استخراج دادههای پنهان (مثل CSRF)
ایده: ابتدا صفحهٔ لاگین را GET میکنیم، توکنهای پنهان (مثلاً csrf_token) را استخراج کرده و سپس با FormRequest.from_response فرم را تکمیل و ارسال میکنیم.
import scrapy
from scrapy import Spider
from scrapy.http import FormRequest
class HiddenDataLoginSpider(Spider):
name = 'hidden_data_login'
def start_requests(self):
login_url = 'http://quotes.toscrape.com/login'
yield scrapy.Request(login_url, callback=self.login)
def login(self, response):
# استخراج توکن CSRF از HTML اولیه
token = response.css('form input[name=csrf_token]::attr(value)').get()
# ارسال فرم همراه با توکن و باقی فیلدها
return FormRequest.from_response(response,
formdata={'csrf_token': token, 'username': 'foobar', 'password': 'foobar'},
callback=self.start_scraping)
def start_scraping(self, response):
# اکنون session لاگین شده و میتوانید ادامه دهید
pass
توضیحات عملی و نکات:
- اگر توکنها در HTML اولیه وجود داشته باشند این روش قابل اعتماد و سبک است.
- همیشه بررسی کنید که نام فیلدها در HTML با چیزی که در formdata میفرستید مطابقت داشته باشد.
- استفاده از FormRequest.from_response کمک میکند فیلدهای مخفی فرم به صورت خودکار شناسایی شوند؛ اما گاهی باید صریحاً مقادیر را تعیین کنید.
روش ۳: استفاده از مرورگر headless (مثال با Scrapy + Splash)
چه زمانی لازم است: وقتی سایت از جاوااسکریپت برای تولید مقادیر مهمِ لاگین (مثل metadata1، aaToken یا رمزنگاری پسورد) استفاده میکند؛ یا جریان تأیید دو مرحلهای و صفحات متعدد دارد. در این موارد بهتر است از مرورگر headless برای انجام تعاملات استفاده کنید و سپس کوکیها را به جلسات Scrapy منتقل کنید.
استراتژی کلی:
- با Splash یا هر مرورگر headless وارد شوید.
- کوکیهای session را استخراج کنید.
- با همین کوکیها درخواستهای بعدی Scrapy را ارسال کنید تا از مزایای Scrapy (pipeline، concurrency کنترلشده، parserها) استفاده کنید.
نمونهٔ Lua script (که در Splash اجرا میشود) و Spider پایتون:
-- lua: تعامل ساده با فرمها و بازگرداندن html و کوکیها
function main(splash, args)
splash:init_cookies(splash.args.cookies)
assert(splash:go(args.url))
assert(splash:wait(1))
splash:set_viewport_full()
local email_input = splash:select('input[name=email]')
email_input:send_text('EMAIL@EXAMPLE.COM')
assert(splash:wait(1))
local continue_btn = splash:select('input[id=continue]')
continue_btn:click()
assert(splash:wait(2))
local password_input = splash:select('input[name=password]')
password_input:send_text('PASSWORD')
assert(splash:wait(1))
local sign_in = splash:select('input[id=signInSubmit]')
sign_in:click()
assert(splash:wait(3))
return { html = splash:html(), url = splash:url(), cookies = splash:get_cookies() }
end
import scrapy
from scrapy_splash import SplashRequest
lua_script = '''
-- (قرار دادن همان کد lua بالا در اینجا)
'''
class AmazonLoginSpider(scrapy.Spider):
name = 'amazon_login'
def start_requests(self):
signin_url = 'https://www.example.com/ap/signin'
yield SplashRequest(url=signin_url, callback=self.after_login, endpoint='execute', args={'lua_source': lua_script, 'width': 1000})
def after_login(self, response):
# تبدیل لیست کوکیها به دیکشنری name:value
cookies_dict = {c['name']: c['value'] for c in response.data.get('cookies', [])}
# حالا با همان کوکیها میتوان درخواستهای معمول Scrapy را ارسال کرد
yield scrapy.Request('https://www.example.com/', cookies=cookies_dict, callback=self.parse)
def parse(self, response):
# پردازش صفحات لاگینشده
with open('response.html', 'wb') as f:
f.write(response.body)
تنظیمات مورد نیاز برای فعالسازی Splash در settings.py (نمونه):
# settings.py (نمونه)
SPLASH_URL = 'http://localhost:8050'
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
SPIDER_MIDDLEWARES = {
'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
نکات فنی مهم:
- استفاده از headless مرورگر، سادهترین راه برای کنار زدن منطق پیچیدهٔ JS است اما هزینهٔ اجرای آن (منابع و زمان) بیشتر است.
- بعد از ورود با مرورگر، استخراج کوکیها و بازاستفادهٔ آنها با درخواستهای Scrapy بهترین ترکیب «پایایی + کارایی» را میدهد.
- اگر دادهها با رمزنگاری کلاینت تولید میشوند، معکوسسازی JS خیلی هزینهبر و ناپایدار است.
چگونه پس از لاگین بلاک نشویم
لاگین شدن احتمال شناسایی شدن را افزایش میدهد چون همهٔ درخواستها به یک حساب کاربری مرتبط هستند. نکات عملی برای کاهش ریسک:
- چند حساب: تقسیم بار روی چند حساب تا از ارسال درخواستهای زیاد از یک حساب جلوگیری شود.
- آیپی ثابت برای هر حساب: تغییر مکرر آیپی هنگام لاگین میتواند محرک هشدارهای امنیتی باشد. پروکسیهای مسکونی یا دیتاسنتر بسته به هدف انتخاب شوند.
- پروفایل مرورگر و User-Agent: از UA واقعی و ثابت استفاده کنید؛ UA پیشفرض Scrapy مشخص و آشکار است.
- تکترد و تأخیر بین درخواستها: CONCURRENT_REQUESTS=1 و DOWNLOAD_DELAY (مثلاً 10 ثانیه با نوسان) رفتار انسانیتری شبیهسازی میکنند.
- الگوهای رفتاری واقعی: مسیرهای بازدید را شبیه کاربر واقعی طراحی کنید (مثلاً جستجو → نتایج → بازدید محصول).
مثالهای تنظیم:
# settings.py
CONCURRENT_REQUESTS = 1
DOWNLOAD_DELAY = 10 # میتواند با randomization ترکیب شود
ریسکها و ملاحظات حقوقی
اسکریپینگ پشت لاگین ریسکهای بیشتری دارد نسبت به صفحات عمومی. نکات کلیدی:
- ممکن است دادههای شخصی (نام، ایمیل، کارت) با حساب مرتبط باشند؛ در صورت فاش شدن مسئولیت حقوقی دارید.
- خطر بسته شدن یا محروم شدن حسابها: سایت ممکن است حسابها را مسدود یا حذف کند و در مواردی از پیگیری قانونی استفاده کند.
- قوانین و Terms of Service: قبل از اسکریپینگ پشت لاگین، حتماً شرایط سرویس را مطالعه کرده و ارزیابی حقوقی انجام دهید؛ در بسیاری از موارد، اقدام به اسکریپینگ خلاف قوانین سایت است.
جمعبندی و توصیههای عملی
خلاصهٔ عملی:
- ابتدا فرایند لاگین را در Developer Tools تحلیل کنید.
- اگر فقط نامکاربری/پسورد لازم است از FormRequest استفاده کنید.
- اگر توکنهای مخفی در HTML وجود دارند، صفحه را GET کنید و آنها را استخراج کرده سپس ارسال کنید.
- برای فرمهای تولیدشده با JS یا رمزنگاریشده، از مرورگر headless استفاده کنید و سپس کوکیها را به Scrapy منتقل کنید.
- همیشه ملاحظات ضدربات: پروکسی مناسب، UA واقعی، تاخیر و الگوهای انسانی را به کار بگیرید.
- پیش از کار، ریسکهای حقوقی و محرمانگی را بررسی کنید.
با رعایت این چارچوبها میتوانید عملاً اکثر صفحات لاگین را مدیریت کنید و از توان Scrapy در پردازش و ذخیرهٔ داده بهره ببرید بدون اینکه پایداری یا امنیت پروژه را به خطر بیندازید.






