چگونه با استفاده از اسکراپی(Scrapy) و پایتون 3 (Python 3) یک صفحه وب را “کراول” (Crawl) کنیم
مقدمه
اسکرپینگ وب (Web Scraping)، یا به عبارتی خراشیدن وب که اغلب با اصطلاح های خزیدن در وب (Web Crawling) یا عنکبوت وب (Web Spidering) و یا “بررسی کردن مجموعه ای صفحات وب و استخراج داده ها توسط برنامه نویسی”، مورد استفاده است، که ابزاری قدرتمند برای کار با داده ها در وب است.
با یک وب اسکریپر، می توانید داده های مربوط به مجموعه ای از محصولات را استخراج کنید، مجموعه بزرگی از داده های متنی یا کمی را برای بازی با آن ها به دست آورید، داده هایی را از یک سایت بدون API رسمی دریافت کنید، یا فقط کنجکاوی خود را ارضا کنید. در این متن آموزشی، با اصول اولیه فرآیند های اسکریپینگ (Scraping) و اسپایدرینگ (Spidering) که در یک مجموعه داده سرگرم کننده کاوش می کنید، آشنا خواهید شد. ما از یک مجموعه Brickset، سایتی که توسط اجتماع عموم اداره می شود که حاوی اطلاعاتی در مورد مجموعه های لگو (LEGO) است، استفاده می کنیم. در پایان این آموزش، شما یک وب اسکریپر پایتون کاملا کاربردی خواهید داشت که از طریق یک سری صفحات “Brickset” را بررسی می کند و و داده های مربوط به مجموعه های لگو را از هر صفحه استخراج می کند و این داده ها را روی صفحه شما نمایش می دهد. اسکریپر به راحتی قابل گسترش خواهد بود، بنابراین می توانید با آن سرگرم شوید و از آن به عنوان پایه ای برای پروژه های خود که داده ها را از وب جدا می کند استفاده کنید.
پیش نیاز ها
برای تکمیل این آموزش به یک محیط توسعه محلی برای پایتون 3 نیاز خواهید داشت. شما می توانید لینک چگونه برای نصب و راه اندازی یک محیط برنامه نویسی محلی برای پایتون 3، برای پیکربندی هر اطلاعاتی که نیاز دارید، استفاده کنید.
مرحله 1 – ایجاد یک اسکریپر پایه
اسکریپینگ یک فرایند دو مرحله ای است:
1. شما به طور سازمان یافته (سیستماتیک) صفحات وب را پیدا و دانلود می کنید.
2. شما می توانید آن صفحات وب را دریافت کنید و اطلاعات را از آن ها استخراج کنید.
هر دوی آن مراحل را می توان به چند روش در بسیاری از زبان ها پیاده سازی کرد.
شما می توانید یک اسکریپر را از ابتدا با استفاده از ساخت ماژول ها یا کتابخانه های ارائه شده توسط زبان برنامه خود بسازید، اما پس از آن باید برای مقابله با برخی از درد سر های احتمالی که با توسعه و پیچیده تر شدن اسکریپر شما اتفاق می افتد، آماده باشید. برای مثال؛ شما باید بتوانید اسکریپ کردن هم زمان بیش از یک صفحه را مدیریت کنید. احتمالا می خواهید بفهمید که چگونه داده های اسکریپ یا خراشیده شده خود را به فرمت های مختلف مانند CSV، XML، یا JSON تبدیل کنید. گاهی اوقات مجبور می شوید با سایت هایی که به تنظیمات و الگوهای دسترسی خاصی نیاز دارند سر و کار داشته باشید.
اگر اسکریپر خود را بالای (مبنی بر) کتابخانه موجود بسازید، شانس بیشتری برای حل این مشکلات خواهید داشت. برای این آموزش، ما از پایتون و اسکریپی (Scrapy) برای ساختن اسکریپر خود استفاده می کنیم.
اسکریپی یکی از محبوب ترین و قدرتمند ترین کتابخانه های اسکریپ پایتون می باشد. برای اسکریپ کردن، روش “Batteries Included” (مشمول باتری) نیاز است، به این معنی که بسیاری از عملکرد های مشترکی را که همه اسکریپر ها به آن نیاز دارند، کنترل می کند تا توسعه دهندگان مجبور نباشند هر بار اسکریپر را باز آفرینی کنند. این کار اسکریپینگ را به یک فرآیند سریع و سرگرم کننده تبدیل می کند!
اسکریپی، مانند بسیاری از بسته های پایتون، در “PyPI” (همچنین به عنوان pip نیز شناخته می شود).
PyPI، شاخص بسته پایتون، یک منبع متعلق به عموم است که از تمام مجموعه نرم افزار های پایتون منتشر شده است.
اگر شما یک پکیج (بسته) نصب پایتون دارید، همانند پکیجی که در پیش نیاز مبحث برای این آموزش مطرح شده است دارید، در حال حاضر
را بر روی دستگاه خود نصب شده است؛ بنا بر این شما می توانید اسکریپی را با روش زیر نصب کنید:
$ pip install scrapy
اگر هنگام نصب با مشکلی مواجه شدید یا می خواهید اسکریپی را بدون استفاده از نصب کنید، اسناد رسمی نصب اسکریپی را بررسی کنید.
با نصب اسکریپی، بیایید یک پوشه جدید برای پروژه خود ایجاد کنیم. می توانید این کار را با دستور در پایانه انجام دهید:
$ mkdir brickset-scraper
اکنون، به فهرست راهنما ی خود (Directory) که الان درست کردید، رجوع کنید:
$ cd brickset-scraper
سپس یک فایل پایتون جدید برای اسکریپی به نام scraper.py ایجاد کنید. ما تمام کد های خود را در این فایل برای این آموزش قرار می دهیم. شما می توانید این فایل را در پایانه با دستور ایجاد کنید؛ همانند دستور زیر:
$ touch scraper.py
یا می توانید فایل را با استفاده از ویرایشگر متن یا مدیر فایل گرافیکی
(Graphical file manager) خود ایجاد کنید.
ما با ساخت یک اسکریپر اصلی و ساده که از اسکریپی به عنوان پایه و اساس خود استفاده می کند، شروع می کنیم. برای این کار، ما یک کلاس پایتون که شامل کلاس فرعی است را می سازیم؛ یک کلاس اسپایدر (Spider) پایه که توسط اسکریپی ارائه شده است. این کلاس دو ویژگی مورد نیاز خواهد داشت:
اسم – انتخاب یک اسم برای اسپایدر
url برای شروع – فهرستی از URL ها شروع به کراول (Crawl) از آن ها می کنیم. با این URL شروع می کنیم.
فایل را در ویرایشگر متن خود باز کنید و این کد را برای ساختن اسپایدر پایه وارد کنید:
import scrapy
class BrickSetSpider(scrapy.Spider):
name = “brickset_spider”
start_urls = [‘http://brickset.com/sets/year-2016’]
بیایید این کد را خط به خط تجزیه کنیم:
ابتدا، ما را وارد می کنیم؛ تا بتوانیم از کلاس هایی که بسته فراهم می کند استفاده کنیم.
سپس ما را که توسط کلاس اسکریپی ارائه شده است را بر می داریم و با استفاده از آن یک کلاس فرعی به نام درست می کنیم. به یک کلاس فرعی به عنوان یک شکل تخصصی تر از کلاس والدین خود تصور کنید. کلاس های فرعی دارای روش ها و رفتار هایی است که نحوه پیروی از URL ها و استخراج داده را از صفحاتی که پیدا می کند تعریف می کند، ولی نمی تواند تشخیص دهد کجا را جستجو کند یا به دنبال چه داده هایی بگردد.
با طبقه بندی آن، می توانیم این اطلاعات را به آن بدهیم.
سپس به اسپایدر اسم را اضافه می کنیم.
در نهایت، ما به اسکریپر خود یک URL می دهیم تا ار آن شروع کند: https://brickset.com/sets/year-2016. اگر این URL را در مرورگر خود باز کنید، شما را به صفحه نتایج جستجو می برد که اولین صفحه از بسیاری از صفحات حاوی مجموعه های لگو را نشان می دهد. حالا بیایید اسکریپر را آزمایش کنیم. شما معمولا فایل های پایتون را با اجرای دستوری مانند اجرا می کنید. با این حال، اسکریپی با خط فاصل دستور خود برای ساده کردن فرآیند شروع یک اسکریپر همراه است. اسکریپر خود را با دستور زیر شروع کنید:
$ scrapy runspider scraper.py
چیزی شبیه به این خواهید دید:
2016-09-22 23:37:45 [scrapy] INFO: Scrapy 1.1.2 started (bot: scrapybot)
2016-09-22 23:37:45 [scrapy] INFO: Overridden settings: {}
2016-09-22 23:37:45 [scrapy] INFO: Enabled extensions:
[‘scrapy.extensions.logstats.LogStats’,
‘scrapy.extensions.telnet.TelnetConsole’,
‘scrapy.extensions.corestats.CoreStats’]
2016-09-22 23:37:45 [scrapy] INFO: Enabled downloader middlewares:
[‘scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware’,
…
‘scrapy.downloadermiddlewares.stats.DownloaderStats’]
2016-09-22 23:37:45 [scrapy] INFO: Enabled spider middlewares:
[‘scrapy.spidermiddlewares.httperror.HttpErrorMiddleware’,
…
‘scrapy.spidermiddlewares.depth.DepthMiddleware’]
2016-09-22 23:37:45 [scrapy] INFO: Enabled item pipelines:
[]
2016-09-22 23:37:45 [scrapy] INFO: Spider opened
2016-09-22 23:37:45 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 2016-09-22 23:37:45 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2016-09-22 23:37:47 [scrapy] DEBUG: Crawled (200) <GET http://brickset.com/sets/year-2016>
2016-09-22 23:37:47 [scrapy] INFO: Closing spider (finished)
2016-09-22 23:37:47 [scrapy] INFO: Dumping Scrapy stats:
{‘downloader/request_bytes’: 224,
‘downloader/request_count’: 1,
…
‘scheduler/enqueued/memory’: 1,
‘start_time’: datetime.datetime(2016, 9, 23, 6, 37, 45, 995167)}
2016-09-22 23:37:47 [scrapy] INFO: Spider closed (finished)
این مقدار زیادی خروجی است، پس بیایید آن را تجزیه کنیم.
• اسکریپر اجزا و افزونه های اضافی را که برای خواندن داده ها از URL نیاز داشت، مقدار دهی و بارگذاری کرد.
• اسکریپر از URL ای که ما در لیست ارائه داده بودیم استفاده کرد و HTML را به دست آورد، همان طور که مرورگر وب شما نیز انجام می دهد.
• اسکریپر آن HTML را به روش منتقل می کند، که به طور پیش فرض کاری انجام نمی دهد. از آن جایی که ما هرگز روش خود را ننوشته ایم، اسپایدر بدون انجام هیچ کاری تمام میشود.
حالا بیایید مقداری داده از صفحه استخراج کنیم.
مرحله دوم – استخراج داده از یک صفحه
ما یک برنامه بسیار ابتدایی ساخته ایم که یک صفحه را فرو می ریزاند، اما هنوز هیچ گونه اسکریپ یا اسپایدری را نمی تواند انجام دهد. بیایید به آن داده هایی برای استخراج بدهیم.
اگر به صفحه ای که می خواهیم اسکریپ کنیم نگاهی بیاندازید، خواهید دید که ساختار زیر را دارد:
• دارای یک سربرگ (Header) است که در هر صفحه وجود دارد.
• برخی از داده های جست و جو سطح بالا، از جمله تعداد موارد منطبق؛ که ما در جست و جوی آن هستیم، و خرده نشانه های (Breadcrumbs) سایت.
• سپس خود مجموعه ها هستند که به شکلی که شبیه جدول یا لیست مرتب است، نمایش داده میشوند. هر مجموعه دارای قالب مشابهی است.
هنگام نوشتن یک اسکریپر، ایده خوبی است که به منبع فایل HTML نگاه کنید و با ساختار آن آشنا شوید. بنابراین اسکریپر آماده است؛ با حذف برخی موارد قابل پیش خوانی:
brickset.com/sets/year-2016
<body>
<section class=”setlist”>
<article class=’set’>
<a href=”https://images.brickset.com/sets/large/10251-1.jpg?201510121127″
class=”highslide plain mainimg” onclick=”return hs.expand(this)”><img
src=”https://images.brickset.com/sets/small/10251-1.jpg?201510121127″ title=”10251-1:
Brick Bank” onError=”this.src=’/assets/images/spacer.png'” /></a>
<div class=”highslide-caption”>
<h1>Brick Bank</h1><div class=’tags floatleft’><a href=’/sets/10251-1/Brick-
Bank’>10251-1</a> <a href=’/sets/theme-Creator-Expert’>Creator Expert</a> <a
class=’subtheme’ href=’/sets/theme-Creator-Expert/subtheme-Modular-
Buildings’>Modular Buildings</a> <a class=’year’ href=’/sets/theme-Creator-
Expert/year-2016′>2016</a> </div><div class=’floatright’>©2016 LEGO
Group</div>
<div class=”pn”>
<a href=”#” onclick=”return hs.previous(this)” title=”Previous (left arrow
key)”>« Previous</a
<a href=”#” onclick=”return hs.next(this)” title=”Next (right arrow key)”>Next
»</a>
</div>
</div>
…
</article>
</section>
</body>
اسکریپ این صفحه یک فرآیند دو مرحله ای است:
1. ابتدا، با جست و جوی قسمت هایی از صفحه که داده های مورد نظر ما را دارند، هر مجموعه لگو را دریافت کنید.
2. سپس، برای هر مجموعه، داده مورد نظر را با بیرون کشیدن داده از تگ HTML به دست آورید.
داده ها را بر اساس انتخابگر ها که ارائه می کنید دریافت می کند. انتخابگر ها الگو هایی هستند که می توانیم از آن ها برای پیدا کردن یک یا چند عنصر در یک صفحه استفاده کنیم تا بتوانیم با داده های درون عنصر کار کنیم.
هر دو انتخابگر CSS یا XPath حمایت می کند.
ما در حال حاضر از انتخابگر های CSS استفاده می کنیم؛ زیرا CSS گزینه ساده و مناسب ترین گزینه برای یافتن همه مجموعه ها در صفحه است. اگر به HTML صفحه نگاه کنید، خواهید دید که هر مجموعه با کلاس خاصی مشخص شده است.
از آن جایی که ما به دنبال کلاس هستیم، از برای انتخابگر CSS خود استفاده می کنیم. تنها کاری که باید انجام دهیم این است که آن انتخابگر را به هدف منتقل کنیم، مانند مثال زیر:
class BrickSetSpider(scrapy.Spider):
name = “brickset_spider”
start_urls = [‘http://brickset.com/sets/year-2016’]
def parse(self, response):
SET_SELECTOR = ‘.set’
for brickset in response.css(SET_SELECTOR):
pass
این کد تمام مجموعه های موجود در صفحه را دریافت می کند و روی آن ها حلقه می زند تا داده ها را استخراج کند. حالا بیایید داده ها را از آن مجموعه ها استخراج کنیم تا بتوانیم آن ها را نمایش دهیم.
نگاهی دیگر به منبع صفحه ای که تجزیه می کنیم؛ به ما می گوید که نام هر مجموعه در یک برچسب برای هر مجموعه ذخیره می شود:
brickset.com/sets/year-2016
<h1>Brick Bank</h1><div class=’tags floatleft’><a href=’/sets/10251-1/Brick-Bank’>10251-1</a>
هدف ما که روی آن حلقه می زنیم، روش خاص خود را دارد، بنابراین می توانیم برای مکان یابی عناصر فرزند (Child Elements) از یک انتخابگر عبور دهیم. کد خود را به صورت زیر تغییر دهید تا نام این مجموعه را مکان یابی کند و نمایش دهد:
class BrickSetSpider(scrapy.Spider):
name = “brickset_spider”
start_urls = [‘http://brickset.com/sets/year-2016’]
def parse(self, response):
SET_SELECTOR = ‘.set’
for brickset in response.css(SET_SELECTOR):
NAME_SELECTOR = ‘h1 ::text’
yield {
‘name’: brickset.css(NAME_SELECTOR).extract_first(),
توجه داشته باشید: ویرگول انتهایی بعد از اشتباه تایپی نیست به زودی موارد بیشتری را به این بخش اضافه خواهیم کرد. بنابراین ویرگول را در آنجا گذاشته ایم تا بعدا اضافه کردن به این بخش را آسان تر کنیم.
متوجه دو چیز در این کد خواهید شد:
• ما را به عنوان اسم برای انتخابگر خود استفاده می کنیم. این شبه-انتخابگر CSS می باشد که متن را داخل برچسب واکشی می کند به جای خود برچسب.
• ما هدف بازگشتی بر روی
را می نامیم؛ زیرا ما اولین عنصر که با انتخابگر مطابق است را می خواهیم. این کار به جای فهرستی از عناصر، رشته (String) می دهد. فایل را ذخیره کنید و اسکریپر را دوباره اجرا کنید:
این بار می بینید که نام مجموعه ها در خروجی ظاهر می شود:
Output
…
[scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘name’: ‘Brick Bank’}
[scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘name’: ‘Volkswagen Beetle’}
[scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘name’: ‘Big Ben’}
[scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘name’: ‘Winter Holiday Train’}
…
بیایید با افزودن انتخابگر های جدید برای تصاویر، قطعات و اشکال کوچک (Miniature Figures) و یا “minifigs” که با یک مجموعه می آید، این کد را گسترش دهیم.
نگاهی دیگر به HTML برای یک مجموعه خاص بیاندازید:
brickset.com/sets/year-2016
<article class=”set”>
<a class=”highslide plain mainimg” href=”http://images.brickset.com/sets/images/10251-1.jpg?201510121127″ onclick=”return hs.expand(this)”>
<img src=”http://images.brickset.com/sets/small/10251-1.jpg?201510121127″ title=”10251-1: Brick Bank”></a>
…
<div class=”meta”>
<h1><a href=”/sets/10251-1/Brick-Bank”><span>10251:</span> Brick Bank</a> </h1>
…
<div class=”col”>
<dl>
<dt>Pieces</dt>
<dd><a class=”plain” href=”/inventories/10251-1″>2380</a></dd>
<dt>Minifigs</dt>
<dd><a class=”plain” href=”/minifigs/inset-10251-1″>5</a></dd>
…
</dl>
</div>
…
</div>
</article>
با بررسی این کد می توانیم چند چیز را ببینیم:
• تصویر برای مجموعه در ویژگی
که در برچسب ، در شروع مجموعه ذخیره شده است. ما می توانیم از انتخابگر CSS دیگری برای واکشی این ارزش استفاده کنیم؛ درست مثل وقتی که نام هر مجموعه را دریافت کردیم.
• به دست آوردن تعداد قطعات (Pieces) کمی دشوارتر است. یک برچسب وجود دارد که شامل متن
می باشد؛ و سپس برچسب که حاوی تعداد واقعی قطعات است، به دنبال آن می آید. ما از XPath؛ ، یک زبان پرس و جو برای پیمایش XML، برای درک این موضوع استفاده خواهیم کرد، زیرا برای نمایش با انتخابگر های CSS بسیار پیچیده است.
• به دست آوردن تعداد مینی فیگور ها (minifigs) در یک مجموعه مشابه به دست آوردن تعداد قطعات است. برچسب که حاوی متن است، که برچسب بلافاصله بعد از آن با شماره به دنبال آن می آید.
بنابراین بیایید اسکریپر را برای دریافت این اطلاعات جدید اصلاح کنیم:
class BrickSetSpider(scrapy.Spider):
name = ‘brick_spider’
start_urls = [‘http://brickset.com/sets/year-2016’]
def parse(self, response):
SET_SELECTOR = ‘.set’
for brickset in response.css(SET_SELECTOR):
NAME_SELECTOR = ‘h1 ::text’
PIECES_SELECTOR = ‘.//dl[dt/text() = “Pieces”]/dd/a/text()’
MINIFIGS_SELECTOR = ‘.//dl[dt/text() = “Minifigs”]/dd[2]/a/text()’
IMAGE_SELECTOR = ‘img ::attr(src)’
yield {
‘name’: brickset.css(NAME_SELECTOR).extract_first(),
‘pieces’: brickset.xpath(PIECES_SELECTOR).extract_first(),
‘minifigs’: brickset.xpath(MINIFIGS_SELECTOR).extract_first(),
‘image’: brickset.css(IMAGE_SELECTOR).extract_first(),
}
تغییرات خود را ذخیره کنید و اسکریپر را دوباره را اجرا کنید:
$ scrapy runspider scraper.py
اکنون داده های جدید را در خروجی برنامه خواهید دید:
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘minifigs’: ‘5’, ‘pieces’: ‘2380’, ‘name’: ‘Brick Bank’, ‘image’: ‘http://images.brickset.com/sets/small/10251-1.jpg?201510121127’}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘minifigs’: None, ‘pieces’: ‘1167’, ‘name’: ‘Volkswagen Beetle’, ‘image’: ‘http://images.brickset.com/sets/small/10252-1.jpg?201606140214’}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘minifigs’: None, ‘pieces’: ‘4163’, ‘name’: ‘Big Ben’, ‘image’: ‘http://images.brickset.com/sets/small/10253-1.jpg?201605190256’}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘minifigs’: None, ‘pieces’: None, ‘name’: ‘Winter Holiday Train’, ‘image’: ‘http://images.brickset.com/sets/small/10254-1.jpg?201608110306’}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘minifigs’: None, ‘pieces’: None, ‘name’: ‘XL Creative Brick Box’, ‘image’: ‘/assets/images/misc/blankbox.gif’}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{‘minifigs’: None, ‘pieces’: ‘583’, ‘name’: ‘Creative Building Set’, ‘image’: ‘http://images.brickset.com/sets/small/10702-1.jpg?201511230710’}
مرحله 3- کراول کردن چندین صفحه
ما با موفقیت داده ها را از آن صفحه اولیه استخراج کردیم، اما برای دیدن بقیه نتایج از آن به عقب نمی رویم. تمام هدف از اسپایدر تشخیص و پیمودن و انتقال دادن لینک ها به صفحات دیگر دریافت داده از آن صفحات است.
متوجه خواهید شد که بالا و پایین هر صفحه دارای یک علامت (Caret)، (>) به سمت راست می باشد که نتایج لینک را به صفحه بعدی می دهد. HTML آن در زیر آمده است:
brickset.com/sets/year-2016
<ul class=”pagelength”>
…
<li class=”next”>
<a href=”http://brickset.com/sets/year-2017/page-2″>›</a>
</li>
<li class=”last”>
<a href=”http://brickset.com/sets/year-2016/page-32″>»</a>
</li>
</ul>
همان طور که که ميبينيد، یک برچسب با کلاس
وجود دارد و داخل آن برچسب، یک برچسب با لینک به صفحه بعدی قرار دارد. تنها کاري که بايد بکنيم اين است که به اسکریپر بگوییم آن لینک را در صورت موجود بودن دنبال کند.
کد خود را به صورت زیر تغییر دهید:
class BrickSetSpider(scrapy.Spider):
name = ‘brick_spider’
start_urls = [‘http://brickset.com/sets/year-2016’]
def parse(self, response):
SET_SELECTOR = ‘.set’
for brickset in response.css(SET_SELECTOR):
NAME_SELECTOR = ‘h1 ::text’
PIECES_SELECTOR = ‘.//dl[dt/text() = “Pieces”]/dd/a/text()’
MINIFIGS_SELECTOR = ‘.//dl[dt/text() = “Minifigs”]/dd[2]/a/text()’
IMAGE_SELECTOR = ‘img ::attr(src)’
yield {
‘name’: brickset.css(NAME_SELECTOR).extract_first(),
‘pieces’: brickset.xpath(PIECES_SELECTOR).extract_first(),
‘minifigs’: brickset.xpath(MINIFIGS_SELECTOR).extract_first(),
‘image’: brickset.css(IMAGE_SELECTOR).extract_first(),
}
NEXT_PAGE_SELECTOR = ‘.next a ::attr(href)’
next_page = response.css(NEXT_PAGE_SELECTOR).extract_first()
if next_page:
yield scrapy.Request(
response.urljoin(next_page),
callback=self.parse
ابتدا یک انتخابگر برای لینک “صفحه بعدی” تعریف می کنیم، اولین تطابق را استخراج می کنیم و بررسی می کنیم که آیا وجود دارد یا خیر. مقداری است که با گفتن “Hey, crawl this page” برمی گردانیم، و
می گوید “وقتی که HTML را از این صفحه دریافت کردید، آن را به این روش برگردانید تا بتوانیم آن را تجزیه کنیم، داده ها را استخراج کنید و صفحه بعدی را پیدا کنید.”
این به آن معنی است که وقتی به صفحه بعدی رفتیم، در آن جا به دنبال لینک به صفحه بعدی خواهیم بود، و در آن صفحه به دنبال لینک صفحه بعدی خواهیم بود و به همین ترتیب، تا زمانی که لینکی برای صفحه بعد پیدا نکنیم. نکته کلیدی برای اسکرایپ کردن وب: یافتن و دنبال کردن لینک ها. در این مثال، کاملا متوالی است؛ یک صفحه لینکی به صفحه بعدی دارد تا زمانی که صفحه آخر را باز کنیم، میتوانید لینک ها به برچسب ها یا سایر نتایج جست و جو، یا هر URL دیگری را که میخواهید دنبال کنید.
در حال حاضر ، اگر شما کد خود را ذخیره کنید و اسپایدر را دوباره اجرا کنید خواهید دید که فقط با تکرار در صفحه اول مجموعه ها متوقف نمی شود. تمام 779 تطابق در 23 صفحه ادامه دارد! در بین طرح کلان مطالب، حجم عظیمی از داده ها نیست، اما اکنون فرآیندی را می دانید که توسط آن به طور خودکار صفحات جدیدی را برای اسکریپ کردن پیدا می کنید.
در اینجا کد تکمیل شده ما برای این آموزش با استفاده از برجسته سازی اختصاصی پایتون آمده است:
import scrapy
class BrickSetSpider(scrapy.Spider):
name = ‘brick_spider’
start_urls = [‘http://brickset.com/sets/year-2016’]
def parse(self, response):
SET_SELECTOR = ‘.set’
for brickset in response.css(SET_SELECTOR):
NAME_SELECTOR = ‘h1 ::text’
PIECES_SELECTOR = ‘.//dl[dt/text() = “Pieces”]/dd/a/text()’
MINIFIGS_SELECTOR = ‘.//dl[dt/text() = “Minifigs”]/dd[2]/a/text()’
IMAGE_SELECTOR = ‘img ::attr(src)’
yield {
‘name’: brickset.css(NAME_SELECTOR).extract_first(),
‘pieces’: brickset.xpath(PIECES_SELECTOR).extract_first(),
‘minifigs’: brickset.xpath(MINIFIGS_SELECTOR).extract_first(),
‘image’: brickset.css(IMAGE_SELECTOR).extract_first(),
}
NEXT_PAGE_SELECTOR = ‘.next a ::attr(href)’
next_page = response.css(NEXT_PAGE_SELECTOR).extract_first()
if next_page:
yield scrapy.Request(
response.urljoin(next_page),
callback=self.parse
)
دیدگاهتان را بنویسید