

در این مقاله قدمبهقدم میفهمیم چگونه با Python Scrapy یک اسکریپر تولیدی برای جمعآوری آگهیهای شغلی از LinkedIn بسازیم. هدف این راهنما این است که پس از خواندن آن بتوانید:
لحن مقاله فنی و آموزشی است و فرض میکنیم خواننده با پایتون و مفاهیم پایهٔ Scrapy آشناست.
ایدهٔ کلی: قبل از نوشتن هر کدی، باید ببینیم آیا صفحه درخواستهای API مستقیم (XHR/fetch) میفرستد که حاوی دادهٔ مشاغل است یا خیر. اگر چنین است، معمولاً سادهتر و مقیاسپذیرتر است که پاسخ API را بخوانیم بهجای رندر کامل HTML صفحه.
مراحل عملی:
نکات:
در این بخش یک اسپایدر ساده را میبینیم که اولین صفحه (اولین 25 آگهی) را از endpoint بازخوانی و پارس میکند. کد نمونه و سپس توضیحات فنی را ارائه میکنیم.
import scrapy
class LinkedJobsSpider(scrapy.Spider):
name = "linkedin_jobs"
api_url = (
'https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search?'
'keywords=python&location=United%2BStates&geoId=103644278&start='
)
def start_requests(self):
first_job_on_page = 0
first_url = self.api_url + str(first_job_on_page)
yield scrapy.Request(url=first_url, callback=self.parse_job, meta={'first_job_on_page': first_job_on_page})
def parse_job(self, response):
first_job_on_page = response.meta['first_job_on_page']
jobs = response.css('li')
for job in jobs:
job_item = {}
job_item['job_title'] = job.css('h3::text').get(default='not-found').strip()
job_item['job_detail_url'] = job.css('.base-card__full-link::attr(href)').get(default='not-found').strip()
job_item['job_listed'] = job.css('time::text').get(default='not-found').strip()
job_item['company_name'] = job.css('h4 a::text').get(default='not-found').strip()
job_item['company_link'] = job.css('h4 a::attr(href)').get(default='not-found')
job_item['company_location'] = job.css('.job-search-card__location::text').get(default='not-found').strip()
yield job_itemتوضیح تابعها و ورودی/خروجی:
<li> را که حاوی آگهیها هستند میخواند. خروجی این تابع آیتمهای دیکشنریشده است که توسط Scrapy به pipeline یا خروجی فایل ارسال میشود.تفسیر خطبهخط (خلاصه):
<li> که هرکدام یک آگهی را نمایش میدهند.ایدهٔ کلی: چون هر پاسخ حداکثر 25 نتیجه برمیگرداند، باید تا زمانی که پاسخ خالی نشد یا وضعیت خطا نداد، پارامتر start را بهصورت افزایشی (+25) ارسال کنیم.
# بخشهای اضافهشده در داخل parse_job بعد از استخراج آیتمها
num_jobs_returned = len(jobs)
if num_jobs_returned > 0:
first_job_on_page = int(first_job_on_page) + 25
next_url = self.api_url + str(first_job_on_page)
yield scrapy.Request(url=next_url, callback=self.parse_job, meta={'first_job_on_page': first_job_on_page})
نکات عملی و بهترین روشها:
LinkedIn یکی از محافظهای پیچیدهٔ ضداسکریپ را دارد که بر مبنای ترکیبی از IP، هدرها، فینگرپرینت مرورگر و رفتار کاربر کار میکند. سه رویکرد عملی وجود دارد:
هشدار قانونی و اخلاقی: تلاش برای دسترسی به بخشهایی که نیاز به ورود (login) دارند ممکن است ریسک حقوقی و نقض شرایط سرویس داشته باشد؛ این راهنما بر جمعآوری دادههای عمومی و بدون لاگین تمرکز دارد.
یک نمونهٔ سادهٔ curl که درخواست را از طریق یک پراکسی سرویس خارجی میفرستد (آدرسها و کلیدها را جایگزین کنید):
curl 'PROXY_API_ENDPOINT?api_key=YOUR_API_KEY&url=https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search?keywords=python&location=United%20States&start=0'نکتههای فنی:
برای سادهتر شدن کار در محیط تولید معمولاً از SDKها یا middlewareهایی استفاده میشود که پراکسی، چرخش UA و تشخیص بنها را مدیریت میکنند. در Scrapy این چیزها معمولاً از طریق تغییرات در settings.py اضافه میشوند.
# settings.py (نمونه)
SCRAPEOPS_API_KEY = 'YOUR_API_KEY'
SCRAPEOPS_PROXY_ENABLED = True
DOWNLOADER_MIDDLEWARES = {
'scrapeops_scrapy_proxy_sdk.scrapeops_scrapy_proxy_sdk.ScrapeOpsScrapyProxySdk': 725,
}
برای نصب پکیج پراکسی یا ابزار مانیتورینگ معمولاً از pip استفاده میشود:
pip install scrapeops-scrapy
pip install scrapeops-scrapy-proxy-sdkمانیتورینگ: در محیط تولید ضروری است که عملکرد اسپایدرها را مانیتور کنید (نرخ خطا، زمان پاسخ، تعداد آیتمها). با یک سیستم مانیتورینگ میتوانید هشدار برای کاهش نرخ خروجی یا افزایش خطاها تنظیم کنید.
# مثال ادغام مانیتورینگ در settings.py
EXTENSIONS = {
'scrapeops_scrapy.extension.ScrapeOpsMonitor': 500,
}
DOWNLOADER_MIDDLEWARES.update({
'scrapeops_scrapy.middleware.retry.RetryMiddleware': 550,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': None,
})نکات کلیدی برای اجرا در مقیاس:
بعد از آماده شدن اسپایدر باید آن را در سرور یا سرویس زمانبندی اجرا کنید. گزینهها شامل اجرای کرون روی یک VM، استفاده از سرویسهای زمانبندی ابری یا ابزارهای مخصوص اسکریپینگ است. نکات عملی:
{
"job_title": "Python",
"job_detail_url": "https://www.linkedin.com/jobs/view/...",
"job_listed": "1 day ago",
"company_name": "Example Corp",
"company_link": "https://www.linkedin.com/company/...",
"company_location": "Texas, United States"
}ساختن یک اسکریپر LinkedIn با Scrapy شامل سه بخش اصلی است: یافتن endpoint مناسب در ابزارهای توسعهٔ مرورگر، نوشتن اسپایدر با مدیریت صفحهبندی و استخراج فیلدها، و در نهایت آمادهسازی برای تولید با توجه به محافظهای ضدربات، پراکسی و مانیتورینگ. با رعایت نکات مدیریت خطا، محدودیتهای نرخ و قوانین سرویس، میتوانید یک راهکار قابل اعتماد و قابل توسعه بسازید.