آموزش شروع کار با کرالر پایتون با مثال عملی

آموزش شروع کار با کرالر پایتون با مثال عملی

مقدمه:

به طور مختصر scrapy یک ابزار برای خزیدن (crawl) در صفحات وب به منظور استخراج بخش‌های مورد نظر و انجام تحلیل‌ روی داده‌های به دست آمده، است.

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

شروع کار با scrapy 

ابتدا مروری اجمالی بر معماری scrapy خواهیم داشت که در تصویر زیر مشاهده می شود:

بخش های مختلف این معماری: 

1- Scrapy Engine

مسئول کنترل جریان داده‌ها بین تمام اجزای سیستم و شروع رویدادها در زمانی است که اقدامات خاصی اتفاق می افتد.

2- Scheduler

بخش Scheduler وظیفه دریافت درخواست ها (requests) از موتور (engine) و صف بندی آن ها (enqueues) بر اساس اولویت پردازش را بر عهده دارد تا در آینده، موتور از این درخواست برای استفاده ی دیگر اجزا استفاده کند. هنگامی که engine به Scheduler اعلام نیاز می کند، Scheduler هر درخواستی که نوبت پردازش آن رسیده است را برای engine فراهم می کند.

3- Downloader

مسئول آوردن صفحات وب و تغذیه آن‌ها به موتور است که هر صفحه را به نوبت برای spider ها ارسال می کند. 

4- Spiders 

به کلاس هایی که توسط برنامه نویس برای parse کردن جواب خزنده ها و extract کردن اجزا مختلف جواب نوشته شده اند، گفته می شود. 

5- Item Pipeline 

هر داده‌ای که توسط spider جداسازی می‌شود (scrap) به بخش pipeline منتقل خواهد شد. کاری که این بخش انجام می دهد عبارت است از: 

  • پاک کردن داده های HTML. منظور پاک سازی تگ های HTML هست که در تحلیل داده کاربردی ندارند.
  • چک کردن مجدد داده‌ها به منظور جلوگیری از تکراری بودن داده‌ها. در صورت تکراری بودن، آن‌ها را از حذف می کند.
  • مرتب سازی داده‌ها در هنگام ذخیره سازی داده‌ها در پایگاه داده یا مرتب سازی داده‌های scrap شده در پایگاه داده.

6- Downloader middlewares

واسط میانی بین downloader و engine برای پردازش درخواست ها قبل از رسیدن به downloader است.

7- Spider middlewares

واسط میانی بین spiders و engine برای پردازش درخواست ها قبل از رسیدن به spider است.

پیاده‌سازی خزنده برای سایت سکان آکادمی

نیازمندی ها: 

برای راه اندازی این پروژه به پایتون 3 روی یکی از توزیع های لینوکس و همچنین یک IDE احتیاج دارید. ما در این مقاله با استفاده از Debian 10  این تمرین را پیش بردیم. شما نیز می توانید قبل از شروع این تمرین از نصب پایتون ورژن 3 به بالا روی لینوکس خود مطمئن شوید. برای این کار می توانید از آموزش نصب پایتون 3 روی دبیان 10 استفاده کنید.  

در ابتدا نیاز هست روی سرور لینوکسی خود مقدمات پروژه ی Scrapy را آماده کنید به همین منظور مراحل زیر را پیش ببرید:

  1. ایجاد پوشه برای پروژه در شاخه opt

ابتدا برای پروژه سکان آکادمی یک پوشه به نام academy-project در شاخه opt با دستورهای زیر ایجاد می کنیم: 

  • برای ورود به شاخه opt دستور زیر را ایجاد می کنیم:
cd /opt
  • سپس با دستور زیر پوشه مورد نظر را می سازیم:
mkdir academy-project
  • با دستور cd درون شاخه academy-project ورود می کنیم

2. نصب pip

ابتدا از نصب pip  اطمینان حاصل کنید. با کمک pip می توانیم کتابخانه ها و پکیج های مربوط به پایتون را نصب کنیم برای نصب pip می توانید از دستور زیر استفاده کنید: 

apt install python-pip

3. نصب scrapy

سپس با دستور زیر scrapy را نصب می کنیم: 

pip install scrapy

سپس با دستور 

pip freeze

از نصب scrapy اطمینان حاصل می کنیم. 

یکی از ویژگی‌های مهم scrapy توانایی خزیدن در چند لینک هم زمان با هم است. لذا ما از سایت سکان آکادمی دو بخش دوره های آموزشی و واژه نامه را scrap خواهیم کرد. 

4. ایجاد پروژه

برای ایجاد یک پروژه scrapy  از دستور زیر در  مسیر  opt/academy-project/ استفاده می کنیم: 

scrapy startproject academy

پس از اجرای دستور بالا پروژه ای به نام academy ساخته می‌شود  که درون IDE خود می توانیم ساختار آن را مشاهده کنیم.

 همان‌طور که مشاهده می‌شود با توجه به توضیحاتی که درباره معماری scrapy داده شد، فایل‌هایی مربوط به هر قسمت از معماری ایجاد شده است. 

5. ساخت یک Spider جدید

Spider های پروژه، درون پوشه ای به نام spiders ساخته می‌شوند. به همین منظور ما درون این پوشه یک کلاس به نام AcademySpider که از کلاس Spider ارث بری می کند، می سازیم.

یک spider ابتدا یک نام مخصوص به خود را دارد. به عبارتی اگر ما در یک پروژه چند spider داشته باشیم هر spider باید نام مخصوص به خودش را داشته باشد. برای تعریف نام spider فیلد name در spider را مقدار دهی می کنیم. نام spider پروژه academy است. 

import scrapy

class AcademySpider(scrapy.Spider):
    name="academy"

یک spider لیستی از url ها را دریافت و سپس به هرکدام با دستور request، درخواست می فرستد. پس از دانلود کامل صفحه برای استخراج داده (scrap) اجزای مختلف صفحه آن را به یک متد به نام parse، پاس می دهد که در ادامه به توضیح آن می پردازیم.

پس کافیست فیلد start_urls که متغیری از نوع لیست است را مقدار دهی کنیم و لیست url های پروژه که می خواهیم داده های آن ذخیره شود را در آن ایجاد کنیم. 

سپس همان طور که گفتیم لازم است متدی به نام parse را در کلاس Spider که ما آن را مطابق با پروژه خودمان override می کنیم، ایجاد کنیم که این متد یک شی با نام response که از نوع TextResponse است را دریافت می کند. 

هنگامی که یک Spider کار خود را شروع می کند متد start_requests را صدا زده و لیست url ها را دریافت و شروع به دانلود آن‌ها می کند. 

البته ما در ادامه فقط با لینک مربوط به دوره ها شروع می کنیم تا نکات لازم را توضیح دهیم سپس لینک مربوط به واژه نامه ها را اضافه کرده و فقط خروجی را مشاهده خواهیم کرد.

6. پیاده‌سازی متد parse:

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

یک متد به نام parse را ایجاد می کنیم. سپس باید در بدنه ی متد، کد مربوط به دریافت موضوع دوره را بنویسیم.

def parse(self, response):

برای جداسازی اجزای مختلف صفحه ای که می خواهیم کرال کنیم، انتخاب گر (selector) های مختلفی را می توانیم استفاده کنیم. از جمله انتخاب به وسیله css یا xpath یا دیگر selector ها. ما در این پروژه از انتخاب گرهای css استفاده خواهیم کرد. ابتدا لینک مربوط به دوره ها را در مرورگر باز کرده و روی یک دوره راست کلیک کرده و آن را  inspect می کنیم. 

با inspect کردن یک دوره  می بینیم این دوره درون یک div با کلاس  col-sm-12 col-md-6 col-lg-4 قرار گرفته است که درون این div تگ های بسیاری دیده می‌شوند.

همان طور که مشاهده می کنید:

  • موضوع پروژه در یک تگ H5 قرار دارد.
  • لینک در یک تگ a قرار دارد.
  • سطح دوره در Div که کلاس course-level course-meta را دارد.

ابتدا تگ div با کلاس col-sm-12 col-md-6 col-lg-4 را جدا کرده و سپس درون متغیری به نام panel ذخیره می کنیم. سپس ما از متغیر panel برای دریافت باقی موارد استفاده می کنیم.

برای دریافت عنوان دوره از panel تگ h5 را پیدا کرده و text درون آن را با متد get در متغیر title ذخیره می کنیم و سپس آن را yield  می کنیم.

def parse(self, response):
 panel = response.css("div[class='col-sm-6 col-md-4']")
 title=panel.css("h5::text").get()
 yield {"cource_title": title}

برای اجرا و مشاهده خروجی از دستور زیر در ترمینال استفاده می کنیم.

 scrapy crawl academy

دستور بالا spider مارا شناسایی و آن را اجرا می کند. 

همان طور که مشاهده می کنید عنوان دوره در خروجی وجود دارد. 

به منظور مشاهده بهتر خروجی با اضافه کردن برخی تنظیمات به فایل settings پروژه، از این به بعد خروجی را دریک فایل z ذخیره کرده و مشاهده می کنیم. برای این کار فایل settings.py را باز کرده و مقدار زیر را به انتهای فایل اضافه کنید. 

FEED_EXPORT_ENCODING=’UTF-8’

اضافه کردن این مقدار به فایل تنظیمات برای پشتیبانی از حروف با encoding یوتی اف 8 hsj تا حروف فارسی در فایل json به درستی نشان داده شوند. 

با توجه به نکته ذکر شده در بالا، پروژه را این بار با دستور زیر اجرا کنید: 

scrapy crawl academy -o academy.json

پس از اجرای دستور بالا یک فایل به نام academy.json در پروژه ایجاد می‌شود که نتیجه کراول ما در آن ذخیره شده است.

اکنون که با برخی دستورهای ساده scrapy آشنا شدیم لینک مربوط به واژه نامه سایت sokanacademy را در لیست urls ها قرار می دهیم. 

به دلیل اینکه یک callback به نام parse داریم و callback دیگری پیاده سازی نکردیم. حالا متد parse را به صورت زیر تغییر می دهیم. 

def parse(self, response):
    page_title=response.url.split("/")[-1]

    if page_title=="academy":
        courses_section = response.css("div[class='academy-index-courses-wrapper']")
        panels = courses_section.css("div[class='col-sm-12 col-md-6 col-lg-4']")
        for panel in panels :
            title = panel.css("h5::text").get()
            level = panel.css("div[class='course-level course-meta']::text").get()
            link = panel.css("a::attr(href)").get()
            yield {"section":page_title,"cource_title": title , "level" :level  , "link" :link }

    if page_title=="glossary":
        panels = response.css("div[class='col-sm-12 col-md-6 col-lg-4']")
        for panel in panels :
            title = panel.css("h4::text").get()
            translate = panel.css("h6::text").get()
            yield {"section":page_title,"word": title , "translate": translate }

به طور خلاصه در کد بالا دو صفحه را کرال کرده و داده های آن را استخراج می کند. صفحه ی مربوط به دوره ها که 9 دوره ی آخر را به همراه اسم و  سطح دوره و  لینک آن و صفحه ی مربوط به فن واژه یا همان واژه نامه که همه ی کلمات به همراه معنی آن را استخراج می کند. 

پروژه را با دستور زیر مجددا اجرا و نتیجه را در خروجی مشاهده می کنیم. 

scrapy crawl academy -o academy.json
[
{"section": "academy", "cource_title": "آموزش گام به گام Elasticsearch", "level": "مقدماتی", "link": "/academy/courses/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%DA%AF%D8%A7%D9%85-%D8%A8%D9%87-%DA%AF%D8%A7%D9%85-elasticsearch"},
{"section": "academy", "cource_title": "آموزش VueJS", "level": "مقدماتی", "link": "/academy/courses/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-vuejs"},
{"section": "academy", "cource_title": "آموزش کاربردی داکر", "level": "مقدماتی", "link": "/academy/courses/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%AF%DB%8C-%D8%AF%D8%A7%DA%A9%D8%B1"},
{"section": "academy", "cource_title": "OWASP TOP10", "level": "مقدماتی", "link": "/academy/courses/owasp-top10"},
{"section": "academy", "cource_title": "GraphQL", "level": "مقدماتی", "link": "/academy/courses/graphql"},
{"section": "academy", "cource_title": "آموزش کاربردی گیت برای برنامه نویسان", "level": "پیشرفته", "link": "/academy/courses/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%AF%DB%8C-%DA%AF%DB%8C%D8%AA-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%D8%A7%D9%86"},
{"section": "academy", "cource_title": "آموزش ردیس", "level": "پیشرفته", "link": "/academy/courses/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%D8%B1%D8%AF%DB%8C%D8%B3"},
{"section": "academy", "cource_title": "آموزش npm", "level": "پیشرفته", "link": "/academy/courses/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-npm"},
{"section": "academy", "cource_title": "آموزش OAuth و Laravel Passport", "level": "پیشرفته", "link": "/academy/courses/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-oauth-%D9%88-laravel-passport"},
{"section": "glossary", "word": "Abstraction", "translate": "(انتزاع)"},
{"section": "glossary", "word": "Encapsulation", "translate": "(كپسوله كردن)"},
{"section": "glossary", "word": "goal gradient effect", "translate": "(اثر شیب هدف)"},
{"section": "glossary", "word": "Semantic Error", "translate": "(خطای سمنتیک)"},
{"section": "glossary", "word": "Syntax (سینتکس)", "translate": "(قواعد نوشتاری زبان برنامه نویسی)"},
{"section": "glossary", "word": "Regression Testing", "translate": "(regression testing)"},
{"section": "glossary", "word": "Interface Testing", "translate": "(تست رابط)"},
{"section": "glossary", "word": "Smoke Testing", "translate": "(تست smoke)"},
{"section": "glossary", "word": "Sanity Testing", "translate": "(تست sanity)"},
{"section": "glossary", "word": "Incremental Integration Testing", "translate": "(تست یکپارچه افزایشی)"},
{"section": "glossary", "word": "Integration Testing", "translate": "(تست ادغام)"},
{"section": "glossary", "word": "Component Testing", "translate": "(تست کامپوننت)"}

در نهایت کد کامل این کرال را اگر بخواهیم تمام دوره ها و واژه هایی که در صفحه جاری نشان داده شده اند ببینیم کلاس AcademySpider را به صورت زیر کامل می کنیم. 

کد کامل پروژه:

import scrapy
class AcademySpider(scrapy.Spider):
    name = "academy"

    start_urls=[
        'https://sokanacademy.com/academy',
        'https://sokanacademy.com/glossary'
    ]
    def parse(self, response):
        page_title=response.url.split("/")[-1]

        if page_title=="academy":
            courses_section = response.css("div[class='academy-index-courses-wrapper']")
            panels = courses_section.css("div[class='col-sm-12 col-md-6 col-lg-4']")
            for panel in panels :
                title = panel.css("h5::text").get()
                level = panel.css("div[class='course-level course-meta']::text").get()
                link = panel.css("a::attr(href)").get()
                yield {"section":page_title,"cource_title": title , "level" :level  , "link" :link }
        if page_title=="glossary":
            panels = response.css("div[class='col-sm-12 col-md-6 col-lg-4']")
            for panel in panels :
                title = panel.css("h4::text").get()
                translate = panel.css("h6::text").get()
                yield {"section":page_title,"word": title , "translate": translate }

منبع:

https://docs.scrapy.org/en/latest/intro/tutorial.html 

نظرات
اگر login نکردی برامون ایمیلت رو بنویس: